many graphics performance tweaks

* use hardware vertex buffers (video memory)
* cache text drawing with vertex buffers instead of framebuffers
* avoid recalculating texture transformation matrix
master
Eduardo Bart 12 years ago
parent b4261a8c7b
commit 5c35938a92

@ -22,26 +22,21 @@
#include "coordsbuffer.h"
void CoordsBuffer::clear()
CoordsBuffer::CoordsBuffer()
{
m_destRects.reset();
m_srcRects.reset();
m_textureCoords.clear();
m_vertices.clear();
m_updateCache = true;
m_hardwareCacheMode = HardwareBuffer::DynamicDraw;
m_hardwareVertexBuffer = nullptr;
m_hardwareTextureVertexBuffer = nullptr;
m_hardwareCached = false;
m_hardwareCaching = false;
}
void CoordsBuffer::addRect(const Rect& dest)
CoordsBuffer::~CoordsBuffer()
{
m_destRects << dest;
m_updateCache = true;
}
void CoordsBuffer::addRect(const Rect& dest, const Rect& src)
{
m_destRects << dest;
m_srcRects << src;
m_updateCache = true;
if(m_hardwareVertexBuffer)
delete m_hardwareVertexBuffer;
if(m_hardwareTextureVertexBuffer)
delete m_hardwareTextureVertexBuffer;
}
void CoordsBuffer::addBoudingRect(const Rect& dest, int innerLineWidth)
@ -58,7 +53,6 @@ void CoordsBuffer::addBoudingRect(const Rect& dest, int innerLineWidth)
addRect(Rect(right - w + 1, top, w, height - w)); // right
addRect(Rect(left + w, bottom - w + 1, width - w, w)); // bottom
addRect(Rect(left, top + w, w, height - w)); // left
m_updateCache = true;
}
void CoordsBuffer::addRepeatedRects(const Rect& dest, const Rect& src)
@ -83,28 +77,38 @@ void CoordsBuffer::addRepeatedRects(const Rect& dest, const Rect& src)
}
partialDest.translate(dest.topLeft());
m_destRects << partialDest;
m_srcRects << partialSrc;
m_vertexBuffer.addRect(partialDest);
m_textureVertexBuffer.addRect(partialSrc);
}
}
m_updateCache = true;
m_hardwareCached = false;
}
void CoordsBuffer::cacheVertexArrays()
void CoordsBuffer::enableHardwareCaching(HardwareBuffer::UsagePattern usagePattern)
{
if(!m_updateCache)
m_hardwareCacheMode = usagePattern;
m_hardwareCaching = true;
m_hardwareCached = false;
}
void CoordsBuffer::updateCaches()
{
if(!m_hardwareCaching || m_hardwareCached)
return;
int numDestRects = m_destRects.size();
int numSrcRects = m_srcRects.size();
m_vertices.clear();
m_textureCoords.clear();
if(m_vertexBuffer.vertexCount() > 0) {
if(!m_hardwareVertexBuffer && m_vertexBuffer.vertexCount() > 0)
m_hardwareVertexBuffer = new HardwareBuffer(HardwareBuffer::VertexBuffer);
m_hardwareVertexBuffer->bind();
m_hardwareVertexBuffer->write((void*)m_vertexBuffer.vertices(), m_vertexBuffer.size() * sizeof(float), m_hardwareCacheMode);
}
for(register int i=0;i<numDestRects;++i) {
m_vertices.addRect(m_destRects[i]);
if(numSrcRects == numDestRects)
m_textureCoords.addRect(m_srcRects[i]);
if(m_textureVertexBuffer.vertexCount() > 0) {
if(!m_hardwareTextureVertexBuffer && m_textureVertexBuffer.vertexCount() > 0)
m_hardwareTextureVertexBuffer = new HardwareBuffer(HardwareBuffer::VertexBuffer);
m_hardwareTextureVertexBuffer->bind();
m_hardwareTextureVertexBuffer->write((void*)m_textureVertexBuffer.vertices(), m_textureVertexBuffer.size() * sizeof(float), m_hardwareCacheMode);
}
m_updateCache = false;
m_hardwareCached = true;
}

@ -24,33 +24,53 @@
#define COORDSBUFFER_H
#include "vertexarray.h"
#include "hardwarebuffer.h"
class CoordsBuffer
{
public:
void clear();
CoordsBuffer();
~CoordsBuffer();
// no texture
void addRect(const Rect& dest);
void addBoudingRect(const Rect& dest, int innerLineWidth);
void clear() {
m_textureVertexBuffer.clear();
m_vertexBuffer.clear();
m_hardwareCached = false;
}
void addRect(const Rect& dest) {
m_vertexBuffer.addRect(dest);
m_hardwareCached = false;
}
void addRect(const Rect& dest, const Rect& src) {
m_vertexBuffer.addRect(dest);
m_textureVertexBuffer.addRect(src);
m_hardwareCached = false;
}
// textured
void addRect(const Rect& dest, const Rect& src);
void addBoudingRect(const Rect& dest, int innerLineWidth);
void addRepeatedRects(const Rect& dest, const Rect& src);
void cacheVertexArrays();
void enableHardwareCaching(HardwareBuffer::UsagePattern usagePattern = HardwareBuffer::DynamicDraw);
void updateCaches();
bool isHardwareCached() { return m_hardwareCached; }
float *getVertexBuffer() const { return m_vertexBuffer.vertices(); }
float *getTextureVertexBuffer() const { return m_textureVertexBuffer.vertices(); }
int getVertexCount() const { return m_vertexBuffer.vertexCount(); }
int getTextureVertexCount() const { return m_textureVertexBuffer.vertexCount(); }
float *getVertices() const { return m_vertices.vertices(); }
float *getTextureCoords() const { return m_textureCoords.vertices(); }
int getVertexCount() const { return m_vertices.vertexCount(); }
int getTextureCoordsCount() const { return m_textureCoords.vertexCount(); }
HardwareBuffer *getHardwareVertexBuffer() { return m_hardwareVertexBuffer; }
HardwareBuffer *getHardwareTextureVertexBuffer() { return m_hardwareTextureVertexBuffer; }
private:
DataBuffer<Rect> m_destRects;
DataBuffer<Rect> m_srcRects;
VertexArray m_vertices;
VertexArray m_textureCoords;
Boolean<true> m_updateCache;
HardwareBuffer *m_hardwareVertexBuffer;
HardwareBuffer *m_hardwareTextureVertexBuffer;
HardwareBuffer::UsagePattern m_hardwareCacheMode;
VertexArray m_vertexBuffer;
VertexArray m_textureVertexBuffer;
bool m_hardwareCached;
bool m_hardwareCaching;
};
#endif

@ -61,20 +61,23 @@ void Font::load(const OTMLNodePtr& fontNode)
}
}
void Font::renderText(const std::string& text,
const Point& startPos,
const Color& color)
void Font::drawText(const std::string& text, const Point& startPos)
{
Size boxSize = g_graphics.getViewportSize() - startPos.toSize();
Rect screenCoords(startPos, boxSize);
renderText(text, screenCoords, Fw::AlignTopLeft, color);
drawText(text, screenCoords, Fw::AlignTopLeft);
}
void Font::drawText(const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align)
{
static CoordsBuffer coordsBuffer;
coordsBuffer.clear();
calculateDrawTextCoords(coordsBuffer, text, screenCoords, align);
g_painter.drawTextureCoords(coordsBuffer, m_texture);
}
void Font::renderText(const std::string& text,
const Rect& screenCoords,
Fw::AlignmentFlag align,
const Color& color)
void Font::calculateDrawTextCoords(CoordsBuffer& coordsBuffer, const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align)
{
// prevent glitches from invalid rects
if(!screenCoords.isValid() || !m_texture)
@ -86,8 +89,6 @@ void Font::renderText(const std::string& text,
Size textBoxSize;
const std::vector<Point>& glyphsPositions = calculateGlyphsPositions(text, align, &textBoxSize);
g_painter.setColor(color);
for(int i = 0; i < textLenght; ++i) {
int glyph = (uchar)text[i];
@ -148,7 +149,7 @@ void Font::renderText(const std::string& text,
}
// render glyph
g_painter.drawTexturedRect(glyphScreenCoords, m_texture, glyphTextureCoords);
coordsBuffer.addRect(glyphScreenCoords, glyphTextureCoords);
}
}

@ -26,6 +26,7 @@
#include "declarations.h"
#include <framework/otml/declarations.h>
#include <framework/graphics/coordsbuffer.h>
class Font
{
@ -36,15 +37,12 @@ public:
void load(const OTMLNodePtr& fontNode);
/// Simple text render starting at startPos
void renderText(const std::string& text,
const Point& startPos,
const Color& color = Color::white);
void drawText(const std::string& text, const Point& startPos);
/// Advanced text render delimited by a screen region and alignment
void renderText(const std::string& text,
const Rect& screenCoords,
Fw::AlignmentFlag align = Fw::AlignTopLeft,
const Color& color = Color::white);
void drawText(const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align = Fw::AlignTopLeft);
void calculateDrawTextCoords(CoordsBuffer& coordsBuffer, const std::string& text, const Rect& screenCoords, Fw::AlignmentFlag align = Fw::AlignTopLeft);
/// Calculate glyphs positions to use on render, also calculates textBoxSize if wanted
const std::vector<Point>& calculateGlyphsPositions(const std::string& text,

@ -1,25 +0,0 @@
/*
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hardwarebuffer.h"

@ -24,8 +24,46 @@
#ifndef HARDWAREBUFFER_H
#define HARDWAREBUFFER_H
#include "declarations.h"
class HardwareBuffer
{
public:
enum Type {
VertexBuffer = GL_ARRAY_BUFFER,
IndexBuffer = GL_ELEMENT_ARRAY_BUFFER
};
enum UsagePattern {
StreamDraw = GL_STREAM_DRAW,
StreamRead = GL_STREAM_READ,
StreamCopy = GL_STREAM_COPY,
StaticDraw = GL_STATIC_DRAW,
StaticRead = GL_STATIC_READ,
StaticCopy = GL_STATIC_COPY,
DynamicDraw = GL_DYNAMIC_DRAW,
DynamicRead = GL_DYNAMIC_READ,
DynamicCopy = GL_DYNAMIC_COPY
};
HardwareBuffer(Type type ) {
m_type = type;
m_id = 0;
glGenBuffers(1, &m_id);
assert(m_id != 0);
}
~HardwareBuffer() {
glDeleteBuffers(1, &m_id);
}
void bind() { glBindBuffer(m_type, m_id); }
static void unbind(Type type) { glBindBuffer(type, 0); }
void write(void *data, int count, UsagePattern usage) { glBufferData(m_type, count, data, usage); }
//void read(void *data, int count);
private:
Type m_type;
GLuint m_id;
};
#endif

@ -55,7 +55,6 @@ void Painter::terminate()
void Painter::drawProgram(const PainterShaderProgramPtr& program, CoordsBuffer& coordsBuffer, PainterShaderProgram::DrawMode drawMode)
{
coordsBuffer.cacheVertexArrays();
if(coordsBuffer.getVertexCount() == 0)
return;

@ -79,42 +79,39 @@ void PainterShaderProgram::setTexture(const TexturePtr& texture)
if(!texture)
return;
float w = texture->getWidth();
float h = texture->getHeight();
Matrix2 textureTransformMatrix = { 1.0f/w, 0.0f,
0.0f, 1.0f/h };
textureTransformMatrix.transpose();
bind();
setUniformTexture(TEXTURE_UNIFORM, texture, 0);
setUniformValue(TEXTURE_TRANSFORM_MATRIX_UNIFORM, textureTransformMatrix);
setUniformValue(TEXTURE_TRANSFORM_MATRIX_UNIFORM, texture->getTransformMatrix());
}
void PainterShaderProgram::draw(const CoordsBuffer& coordsBuffer, DrawMode drawMode)
void PainterShaderProgram::draw(CoordsBuffer& coordsBuffer, DrawMode drawMode)
{
bind();
setUniformValue(TIME_UNIFORM, g_clock.timeElapsed(m_startTime));
int numVertices = coordsBuffer.getVertexCount();
if(numVertices == 0)
int vertexCount = coordsBuffer.getVertexCount();
if(vertexCount == 0)
return;
bool mustDisableVertexArray = false;
if(coordsBuffer.getVertexCount() > 0) {
enableAttributeArray(PainterShaderProgram::VERTEX_COORDS_ATTR);
setAttributeArray(PainterShaderProgram::VERTEX_COORDS_ATTR, coordsBuffer.getVertices(), 2);
mustDisableVertexArray = true;
}
coordsBuffer.updateCaches();
bool hardwareCached = coordsBuffer.isHardwareCached();
bool mustDisableTexCoordsArray = false;
if(coordsBuffer.getTextureCoordsCount() > 0) {
enableAttributeArray(PainterShaderProgram::VERTEX_COORDS_ATTR);
if(hardwareCached)
coordsBuffer.getHardwareVertexBuffer()->bind();
setAttributeArray(PainterShaderProgram::VERTEX_COORDS_ATTR, hardwareCached ? 0 : coordsBuffer.getVertexBuffer(), 2);
if(coordsBuffer.getTextureVertexCount() != 0) {
enableAttributeArray(PainterShaderProgram::TEXTURE_COORDS_ATTR);
setAttributeArray(PainterShaderProgram::TEXTURE_COORDS_ATTR, coordsBuffer.getTextureCoords(), 2);
mustDisableTexCoordsArray = true;
if(hardwareCached)
coordsBuffer.getHardwareTextureVertexBuffer()->bind();
setAttributeArray(PainterShaderProgram::TEXTURE_COORDS_ATTR, hardwareCached ? 0 : coordsBuffer.getTextureVertexBuffer(), 2);
}
if(hardwareCached)
HardwareBuffer::unbind(HardwareBuffer::VertexBuffer);
for(int i=0;i<(int)m_textures.size();++i) {
int location = std::get<0>(m_textures[i]);
if(location == -1)
@ -127,14 +124,11 @@ void PainterShaderProgram::draw(const CoordsBuffer& coordsBuffer, DrawMode drawM
}
glActiveTexture(GL_TEXTURE0);
glDrawArrays(drawMode, 0, numVertices);
glDrawArrays(drawMode, 0, vertexCount);
if(mustDisableVertexArray)
disableAttributeArray(PainterShaderProgram::VERTEX_COORDS_ATTR);
disableAttributeArray(PainterShaderProgram::VERTEX_COORDS_ATTR);
if(mustDisableTexCoordsArray)
if(coordsBuffer.getTextureVertexCount() != 0)
disableAttributeArray(PainterShaderProgram::TEXTURE_COORDS_ATTR);
//release();
}

@ -54,7 +54,7 @@ public:
void setOpacity(float opacity);
void setTexture(const TexturePtr& texture);
void setUniformTexture(int location, const TexturePtr& texture, int index);
void draw(const CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles);
void draw(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles);
private:
DrawMode m_drawMode;

@ -45,6 +45,8 @@ Texture::~Texture()
uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int height)
{
m_size.resize(width, height);
m_transformMatrix = { 1.0f/width, 0.0f,
0.0f, 1.0f/height };
// gets max texture size supported by the driver
static GLint maxTexSize = -1;
@ -63,6 +65,7 @@ uint Texture::internalLoadGLTexture(uchar *pixels, int channels, int width, int
// generate gl texture
GLuint id;
glGenTextures(1, &id);
assert(id != 0);
glBindTexture(GL_TEXTURE_2D, id);
// detect pixels GL format

@ -47,6 +47,7 @@ public:
int getWidth() { return m_size.width(); }
int getHeight() { return m_size.height(); }
const Size& getSize() { return m_size; }
const Matrix2& getTransformMatrix() { return m_transformMatrix; }
bool isEmpty() { return m_textureId == 0; }
bool hasMipmaps() { return m_hasMipmaps; }
@ -57,6 +58,7 @@ protected:
GLuint m_textureId;
Size m_size;
Matrix2 m_transformMatrix;
Boolean<false> m_hasMipmaps;
Boolean<false> m_smooth;
};

@ -59,6 +59,7 @@ public:
void clear() { m_buffer.reset(); }
float *vertices() const { return m_buffer.data(); }
int vertexCount() const { return m_buffer.size() / 2; }
int size() const { return m_buffer.size(); }
private:
DataBuffer<float> m_buffer;

@ -47,7 +47,8 @@ void UIFrameCounter::draw()
}
m_frameCount++;
m_font->renderText(m_fpsText, m_rect, m_align, Color::white);
g_painter.setColor(Color::white);
m_font->drawText(m_fpsText, m_rect, m_align);
}
void UIFrameCounter::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode)

@ -362,7 +362,7 @@ public:
// image
private:
void initImage() { }
void initImage();
void parseImageStyle(const OTMLNodePtr& styleNode);
void updateImageCache() { m_imageMustRecache = true; }
@ -427,8 +427,8 @@ private:
void parseTextStyle(const OTMLNodePtr& styleNode);
Boolean<true> m_textMustRecache;
FrameBufferPtr m_textFramebuffer;
Size m_textCachedBoxSize;
CoordsBuffer m_textCoordsBuffer;
Rect m_textCachedScreenCoords;
protected:
void drawText(const Rect& screenCoords);

@ -25,6 +25,11 @@
#include <framework/graphics/texture.h>
#include <framework/graphics/texturemanager.h>
void UIWidget::initImage()
{
m_imageCoordsBuffer.enableHardwareCaching();
}
void UIWidget::parseImageStyle(const OTMLNodePtr& styleNode)
{
for(const OTMLNodePtr& node : styleNode->children()) {

@ -30,6 +30,7 @@ void UIWidget::initText()
{
m_font = g_fonts.getDefaultFont();
m_textAlign = Fw::AlignCenter;
m_textCoordsBuffer.enableHardwareCaching();
}
void UIWidget::parseTextStyle(const OTMLNodePtr& styleNode)
@ -51,36 +52,16 @@ void UIWidget::drawText(const Rect& screenCoords)
if(m_text.length() == 0 || m_color.aF() == 0.0f)
return;
#if 0
//TODO: creating framebuffers on the fly was slowing down the render
// we should use vertex arrys instead of this method
Size boxSize = screenCoords.size();
if(boxSize != m_textCachedBoxSize || m_textMustRecache) {
if(!m_textFramebuffer)
m_textFramebuffer = FrameBufferPtr(new FrameBuffer(boxSize));
else
m_textFramebuffer->resize(boxSize);
m_textFramebuffer->bind();
Rect virtualTextRect(0, 0, boxSize);
virtualTextRect.translate(m_textOffset);
g_painter.saveAndResetState();
g_painter.setCompositionMode(Painter::CompositionMode_DestBlending);
m_font->renderText(m_text, virtualTextRect, m_textAlign, Color::white);
g_painter.restoreSavedState();
m_textFramebuffer->release();
if(screenCoords != m_textCachedScreenCoords || m_textMustRecache) {
m_textMustRecache = false;
m_textCachedBoxSize = boxSize;
m_textCachedScreenCoords = screenCoords;
m_textCoordsBuffer.clear();
m_font->calculateDrawTextCoords(m_textCoordsBuffer, m_text, screenCoords, m_textAlign);
}
g_painter.setColor(m_color);
m_textFramebuffer->draw(screenCoords);
#else
Rect textRect = screenCoords;
textRect.translate(m_textOffset);
m_font->renderText(m_text, textRect, m_textAlign, m_color);
#endif
g_painter.drawTextureCoords(m_textCoordsBuffer, m_font->getTexture());
}
void UIWidget::onTextChange(const std::string& text, const std::string& oldText)

@ -40,7 +40,8 @@ void AnimatedText::draw(const Point& dest, const Rect& visibleRect)
if(visibleRect.contains(rect)) {
//TODO: cache into a framebuffer
m_font->renderText(m_text, rect, Fw::AlignLeft, m_color);
g_painter.setColor(m_color);
m_font->drawText(m_text, rect, Fw::AlignLeft);
}
}

@ -232,7 +232,7 @@ void Creature::drawInformation(const Point& point, bool useGray, const Rect& par
g_painter.drawFilledRect(healthRect);
if(m_informationFont)
m_informationFont->renderText(m_name, textRect, Fw::AlignTopCenter, fillColor);
m_informationFont->drawText(m_name, textRect, Fw::AlignTopCenter);
if(m_skull != Otc::SkullNone && m_skullTexture) {
g_painter.setColor(Color::white);

@ -40,7 +40,8 @@ void StaticText::draw(const Point& dest, const Rect& parentRect)
// draw only if the real center is not too far from the parent center, or its a yell
if((boundRect.center() - rect.center()).length() < parentRect.width() / 15 || isYell()) {
//TODO: cache into a framebuffer
m_font->renderText(m_text, boundRect, Fw::AlignCenter, m_color);
g_painter.setColor(m_color);
m_font->drawText(m_text, boundRect, Fw::AlignCenter);
}
}

@ -42,7 +42,8 @@ void UIItem::draw()
if(m_font && m_item->isStackable() && m_item->getCount() > 1) {
std::string count = Fw::tostring(m_item->getCount());
m_font->renderText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight, Color(231, 231, 231));
g_painter.setColor(Color(231, 231, 231));
m_font->drawText(count, Rect(m_rect.topLeft(), m_rect.bottomRight() - Point(3, 0)), Fw::AlignBottomRight);
}
//m_font->renderText(Fw::unsafeCast<std::string>(m_item->getId()), m_rect);

Loading…
Cancel
Save