Track 'tainted' area of Bitmaps to optimize blit operations
The 'tainted' area of a Bitmap describes what parts are no longer in a 'cleared' state. When we blit to a fully cleared are of a Bitmap at full opacity, we can completely disregard the existing pixels in the operation, meaning we can skip any blending calculations and just blit / upload straight to the texture. This greatly speeds up text message rendering. In the process, pixman has become a new dependency for mkxp, but the results of this optimization are well worth it!
This commit is contained in:
parent
e903d8cb0f
commit
32361e513a
|
@ -42,6 +42,7 @@ To select this backend, run `qmake BINDING=BINDING_NULL`
|
|||
* SDL2
|
||||
* SDL2_image
|
||||
* SDL2_ttf
|
||||
* pixman
|
||||
* sfml-system 2.0
|
||||
* sfml-audio 2.0
|
||||
* zlib (only ruby backends)
|
||||
|
|
2
mkxp.pro
2
mkxp.pro
|
@ -14,7 +14,7 @@ CONFIG += $$BINDING
|
|||
|
||||
unix {
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG += sigc++-2.0 sdl2 SDL2_image SDL2_ttf sfml-audio
|
||||
PKGCONFIG += sigc++-2.0 sdl2 SDL2_image SDL2_ttf pixman-1 sfml-audio
|
||||
LIBS += -lGLEW -lphysfs -lz
|
||||
}
|
||||
|
||||
|
|
142
src/bitmap.cpp
142
src/bitmap.cpp
|
@ -24,6 +24,9 @@
|
|||
#include "SDL2/SDL.h"
|
||||
#include "SDL2/SDL_image.h"
|
||||
#include "SDL2/SDL_ttf.h"
|
||||
#include "SDL2/SDL_rect.h"
|
||||
|
||||
#include "pixman.h"
|
||||
|
||||
#include "gl-util.h"
|
||||
#include "quad.h"
|
||||
|
@ -63,10 +66,62 @@ struct BitmapPrivate
|
|||
* any context other than as Tilesets */
|
||||
SDL_Surface *megaSurface;
|
||||
|
||||
/* The 'tainted' area describes which parts of the
|
||||
* bitmap are not cleared, ie. don't have 0 opacity.
|
||||
* If we're blitting / drawing text to a cleared part
|
||||
* with full opacity, we can disregard any old contents
|
||||
* in the texture and blit to it directly, saving
|
||||
* ourselves the expensive blending calculation */
|
||||
pixman_region16_t tainted;
|
||||
|
||||
BitmapPrivate()
|
||||
: megaSurface(0)
|
||||
{
|
||||
font = &gState->defaultFont();
|
||||
pixman_region_init(&tainted);
|
||||
}
|
||||
|
||||
~BitmapPrivate()
|
||||
{
|
||||
pixman_region_fini(&tainted);
|
||||
}
|
||||
|
||||
void clearTaintedArea()
|
||||
{
|
||||
pixman_region_clear(&tainted);
|
||||
}
|
||||
|
||||
void addTaintedArea(const IntRect &rect)
|
||||
{
|
||||
pixman_region_union_rect
|
||||
(&tainted, &tainted, rect.x, rect.y, rect.w, rect.h);
|
||||
}
|
||||
|
||||
void substractTaintedArea(const IntRect &rect)
|
||||
{
|
||||
if (!touchesTaintedArea(rect))
|
||||
return;
|
||||
|
||||
pixman_region16_t m_reg;
|
||||
pixman_region_init_rect(&m_reg, rect.x, rect.y, rect.w, rect.h);
|
||||
|
||||
pixman_region_subtract(&tainted, &m_reg, &tainted);
|
||||
|
||||
pixman_region_fini(&m_reg);
|
||||
}
|
||||
|
||||
bool touchesTaintedArea(const IntRect &rect)
|
||||
{
|
||||
pixman_box16_t box;
|
||||
box.x1 = rect.x;
|
||||
box.y1 = rect.y;
|
||||
box.x2 = rect.x + rect.w;
|
||||
box.y2 = rect.y + rect.h;
|
||||
|
||||
pixman_region_overlap_t result =
|
||||
pixman_region_contains_rectangle(&tainted, &box);
|
||||
|
||||
return result != PIXMAN_REGION_OUT;
|
||||
}
|
||||
|
||||
void bindTexture(ShaderBase &shader)
|
||||
|
@ -188,6 +243,8 @@ Bitmap::Bitmap(const char *filename)
|
|||
|
||||
SDL_FreeSurface(imgSurf);
|
||||
}
|
||||
|
||||
p->addTaintedArea(rect());
|
||||
}
|
||||
|
||||
Bitmap::Bitmap(int width, int height)
|
||||
|
@ -265,18 +322,20 @@ void Bitmap::stretchBlt(const IntRect &destRect,
|
|||
{
|
||||
return;
|
||||
}
|
||||
// else if (opacity == 255) /* Fast blit */
|
||||
// {
|
||||
// flush();
|
||||
|
||||
// FBO::bind(source.p->tex.fbo, FBO::Read);
|
||||
// FBO::bind(p->tex.fbo, FBO::Draw);
|
||||
|
||||
// FBO::blit(sourceRect.x, sourceRect.y, sourceRect.w, sourceRect.h,
|
||||
// destRect.x, destRect.y, destRect.w, destRect.h);
|
||||
// }
|
||||
else /* Fragment pipeline */
|
||||
else if (opacity == 255 && !p->touchesTaintedArea(destRect))
|
||||
{
|
||||
/* Fast blit */
|
||||
flush();
|
||||
|
||||
FBO::bind(source.p->tex.fbo, FBO::Read);
|
||||
FBO::bind(p->tex.fbo, FBO::Draw);
|
||||
|
||||
FBO::blit(sourceRect.x, sourceRect.y, sourceRect.w, sourceRect.h,
|
||||
destRect.x, destRect.y, destRect.w, destRect.h);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Fragment pipeline */
|
||||
flush();
|
||||
|
||||
float normOpacity = (float) opacity / 255.0f;
|
||||
|
@ -311,6 +370,8 @@ void Bitmap::stretchBlt(const IntRect &destRect,
|
|||
p->popViewport();
|
||||
}
|
||||
|
||||
p->addTaintedArea(destRect);
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
|
@ -329,6 +390,13 @@ void Bitmap::fillRect(const IntRect &rect, const Vec4 &color)
|
|||
|
||||
p->fillRect(rect, color);
|
||||
|
||||
if (color.w == 0)
|
||||
/* Clear op */
|
||||
p->substractTaintedArea(rect);
|
||||
else
|
||||
/* Fill op */
|
||||
p->addTaintedArea(rect);
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
|
@ -380,6 +448,8 @@ void Bitmap::gradientFillRect(const IntRect &rect,
|
|||
|
||||
p->popViewport();
|
||||
|
||||
p->addTaintedArea(rect);
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
|
@ -416,6 +486,8 @@ void Bitmap::clear()
|
|||
|
||||
glState.clearColor.pop();
|
||||
|
||||
p->clearTaintedArea();
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
|
@ -447,6 +519,8 @@ void Bitmap::setPixel(int x, int y, const Vec4 &color)
|
|||
|
||||
p->pointArray.append(Vec2(x+.5, y+.5), color);
|
||||
|
||||
p->addTaintedArea(IntRect(x, y, 1, 1));
|
||||
|
||||
modified();
|
||||
}
|
||||
|
||||
|
@ -514,9 +588,12 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
|
|||
flush();
|
||||
|
||||
TTF_Font *font = p->font->getSdlFont();
|
||||
Color *fontColor = p->font->getColor();
|
||||
|
||||
SDL_Color c;
|
||||
p->font->getColor()->toSDLColor(c);
|
||||
fontColor->toSDLColor(c);
|
||||
|
||||
float txtAlpha = fontColor->norm.w;
|
||||
|
||||
SDL_Surface *txtSurf;
|
||||
|
||||
|
@ -559,7 +636,35 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
|
|||
Vec2i gpTexSize;
|
||||
gState->ensureTexSize(txtSurf->w, txtSurf->h, gpTexSize);
|
||||
|
||||
// if (str[1] != '\0')
|
||||
IntRect drawnRect = posRect;
|
||||
|
||||
bool fastBlit = !p->touchesTaintedArea(drawnRect) && txtAlpha == 1.0;
|
||||
|
||||
if (fastBlit)
|
||||
{
|
||||
if (squeeze == 1.0)
|
||||
{
|
||||
/* Even faster: upload directly to bitmap texture */
|
||||
TEX::bind(p->tex.tex);
|
||||
TEX::uploadSubImage(posRect.x, posRect.y, posRect.w, posRect.h, txtSurf->pixels, GL_RGBA);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Squeezing involved: need to use intermediary TexFBO */
|
||||
TEXFBO &gpTF = gState->gpTexFBO(txtSurf->w, txtSurf->h);
|
||||
|
||||
TEX::bind(gpTF.tex);
|
||||
TEX::uploadSubImage(0, 0, txtSurf->w, txtSurf->h, txtSurf->pixels, GL_RGBA);
|
||||
|
||||
FBO::bind(gpTF.fbo, FBO::Read);
|
||||
p->bindFBO();
|
||||
|
||||
FBO::blit(0, 0, txtSurf->w, txtSurf->h,
|
||||
posRect.x, posRect.y, posRect.w, posRect.h,
|
||||
FBO::Linear);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Aquire a partial copy of the destination
|
||||
* buffer we're about to render to */
|
||||
|
@ -579,8 +684,7 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
|
|||
shader.setSource();
|
||||
shader.setDestination(gpTex2.tex);
|
||||
shader.setSubRect(bltRect);
|
||||
shader.setOpacity(p->font->getColor()->norm.w);
|
||||
}
|
||||
shader.setOpacity(txtAlpha);
|
||||
|
||||
gState->bindTex();
|
||||
TEX::uploadSubImage(0, 0, txtSurf->w, txtSurf->h, txtSurf->pixels, GL_BGRA_EXT);
|
||||
|
@ -589,16 +693,20 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
|
|||
Quad &quad = gState->gpQuad();
|
||||
quad.setTexRect(FloatRect(0, 0, txtSurf->w, txtSurf->h));
|
||||
quad.setPosRect(posRect);
|
||||
SDL_FreeSurface(txtSurf);
|
||||
|
||||
p->bindFBO();
|
||||
p->pushSetViewport(gState->bltShader());
|
||||
p->pushSetViewport(shader);
|
||||
|
||||
glState.blendMode.pushSet(BlendNone);
|
||||
|
||||
quad.draw();
|
||||
|
||||
glState.blendMode.pop();
|
||||
p->popViewport();
|
||||
}
|
||||
|
||||
SDL_FreeSurface(txtSurf);
|
||||
p->addTaintedArea(drawnRect);
|
||||
|
||||
modified();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue