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
					
				
					 3 changed files with 142 additions and 33 deletions
				
			
		| 
						 | 
					@ -42,6 +42,7 @@ To select this backend, run `qmake BINDING=BINDING_NULL`
 | 
				
			||||||
* SDL2
 | 
					* SDL2
 | 
				
			||||||
* SDL2_image
 | 
					* SDL2_image
 | 
				
			||||||
* SDL2_ttf
 | 
					* SDL2_ttf
 | 
				
			||||||
 | 
					* pixman
 | 
				
			||||||
* sfml-system 2.0
 | 
					* sfml-system 2.0
 | 
				
			||||||
* sfml-audio 2.0
 | 
					* sfml-audio 2.0
 | 
				
			||||||
* zlib (only ruby backends)
 | 
					* zlib (only ruby backends)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								mkxp.pro
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								mkxp.pro
									
										
									
									
									
								
							| 
						 | 
					@ -14,7 +14,7 @@ CONFIG += $$BINDING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unix {
 | 
					unix {
 | 
				
			||||||
    CONFIG += link_pkgconfig
 | 
					    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
 | 
						LIBS += -lGLEW -lphysfs -lz
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										142
									
								
								src/bitmap.cpp
									
										
									
									
									
								
							
							
						
						
									
										142
									
								
								src/bitmap.cpp
									
										
									
									
									
								
							| 
						 | 
					@ -24,6 +24,9 @@
 | 
				
			||||||
#include "SDL2/SDL.h"
 | 
					#include "SDL2/SDL.h"
 | 
				
			||||||
#include "SDL2/SDL_image.h"
 | 
					#include "SDL2/SDL_image.h"
 | 
				
			||||||
#include "SDL2/SDL_ttf.h"
 | 
					#include "SDL2/SDL_ttf.h"
 | 
				
			||||||
 | 
					#include "SDL2/SDL_rect.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "pixman.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "gl-util.h"
 | 
					#include "gl-util.h"
 | 
				
			||||||
#include "quad.h"
 | 
					#include "quad.h"
 | 
				
			||||||
| 
						 | 
					@ -63,10 +66,62 @@ struct BitmapPrivate
 | 
				
			||||||
	 * any context other than as Tilesets */
 | 
						 * any context other than as Tilesets */
 | 
				
			||||||
	SDL_Surface *megaSurface;
 | 
						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()
 | 
						BitmapPrivate()
 | 
				
			||||||
	    : megaSurface(0)
 | 
						    : megaSurface(0)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		font = &gState->defaultFont();
 | 
							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)
 | 
						void bindTexture(ShaderBase &shader)
 | 
				
			||||||
| 
						 | 
					@ -188,6 +243,8 @@ Bitmap::Bitmap(const char *filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		SDL_FreeSurface(imgSurf);
 | 
							SDL_FreeSurface(imgSurf);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p->addTaintedArea(rect());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Bitmap::Bitmap(int width, int height)
 | 
					Bitmap::Bitmap(int width, int height)
 | 
				
			||||||
| 
						 | 
					@ -265,18 +322,20 @@ void Bitmap::stretchBlt(const IntRect &destRect,
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
//	else if (opacity == 255) /* Fast blit */
 | 
						else if (opacity == 255 && !p->touchesTaintedArea(destRect))
 | 
				
			||||||
//	{
 | 
					 | 
				
			||||||
//		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 */
 | 
					 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
 | 
							/* 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();
 | 
							flush();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		float normOpacity = (float) opacity / 255.0f;
 | 
							float normOpacity = (float) opacity / 255.0f;
 | 
				
			||||||
| 
						 | 
					@ -311,6 +370,8 @@ void Bitmap::stretchBlt(const IntRect &destRect,
 | 
				
			||||||
		p->popViewport();
 | 
							p->popViewport();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p->addTaintedArea(destRect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	modified();
 | 
						modified();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -329,6 +390,13 @@ void Bitmap::fillRect(const IntRect &rect, const Vec4 &color)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p->fillRect(rect, color);
 | 
						p->fillRect(rect, color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (color.w == 0)
 | 
				
			||||||
 | 
							/* Clear op */
 | 
				
			||||||
 | 
							p->substractTaintedArea(rect);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							/* Fill op */
 | 
				
			||||||
 | 
							p->addTaintedArea(rect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	modified();
 | 
						modified();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -380,6 +448,8 @@ void Bitmap::gradientFillRect(const IntRect &rect,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p->popViewport();
 | 
						p->popViewport();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p->addTaintedArea(rect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	modified();
 | 
						modified();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -416,6 +486,8 @@ void Bitmap::clear()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	glState.clearColor.pop();
 | 
						glState.clearColor.pop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p->clearTaintedArea();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	modified();
 | 
						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->pointArray.append(Vec2(x+.5, y+.5), color);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						p->addTaintedArea(IntRect(x, y, 1, 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	modified();
 | 
						modified();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -514,9 +588,12 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
 | 
				
			||||||
	flush();
 | 
						flush();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TTF_Font *font = p->font->getSdlFont();
 | 
						TTF_Font *font = p->font->getSdlFont();
 | 
				
			||||||
 | 
						Color *fontColor = p->font->getColor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SDL_Color c;
 | 
						SDL_Color c;
 | 
				
			||||||
	p->font->getColor()->toSDLColor(c);
 | 
						fontColor->toSDLColor(c);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float txtAlpha = fontColor->norm.w;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	SDL_Surface *txtSurf;
 | 
						SDL_Surface *txtSurf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -559,7 +636,35 @@ void Bitmap::drawText(const IntRect &rect, const char *str, int align)
 | 
				
			||||||
	Vec2i gpTexSize;
 | 
						Vec2i gpTexSize;
 | 
				
			||||||
	gState->ensureTexSize(txtSurf->w, txtSurf->h, 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
 | 
							/* Aquire a partial copy of the destination
 | 
				
			||||||
		 * buffer we're about to render to */
 | 
							 * 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.setSource();
 | 
				
			||||||
		shader.setDestination(gpTex2.tex);
 | 
							shader.setDestination(gpTex2.tex);
 | 
				
			||||||
		shader.setSubRect(bltRect);
 | 
							shader.setSubRect(bltRect);
 | 
				
			||||||
		shader.setOpacity(p->font->getColor()->norm.w);
 | 
							shader.setOpacity(txtAlpha);
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		gState->bindTex();
 | 
							gState->bindTex();
 | 
				
			||||||
		TEX::uploadSubImage(0, 0, txtSurf->w, txtSurf->h, txtSurf->pixels, GL_BGRA_EXT);
 | 
							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 &quad = gState->gpQuad();
 | 
				
			||||||
		quad.setTexRect(FloatRect(0, 0, txtSurf->w, txtSurf->h));
 | 
							quad.setTexRect(FloatRect(0, 0, txtSurf->w, txtSurf->h));
 | 
				
			||||||
		quad.setPosRect(posRect);
 | 
							quad.setPosRect(posRect);
 | 
				
			||||||
	SDL_FreeSurface(txtSurf);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		p->bindFBO();
 | 
							p->bindFBO();
 | 
				
			||||||
	p->pushSetViewport(gState->bltShader());
 | 
							p->pushSetViewport(shader);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		glState.blendMode.pushSet(BlendNone);
 | 
							glState.blendMode.pushSet(BlendNone);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		quad.draw();
 | 
							quad.draw();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		glState.blendMode.pop();
 | 
							glState.blendMode.pop();
 | 
				
			||||||
		p->popViewport();
 | 
							p->popViewport();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SDL_FreeSurface(txtSurf);
 | 
				
			||||||
 | 
						p->addTaintedArea(drawnRect);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	modified();
 | 
						modified();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue