Okay, today’s entry is about optimizing tiled surfaces by caching them.
Here’s the deal – we have tiled surfaces, surfaces where a little square is repeated all over.
It’s great for backgrounds, as people who had a Geocities page in 1999 know (if you did not: http://wonder-tonic.com/geocitiesizer/index.php)
An example of a tiled image is the default system menu:
Another is dialogue balloons in Rosemary:
Each tile is handled more or less as a stand alone surface at every redraw, which means: huge overhead.
The fix is: cache them.
This involves hacking the renderer a bit so that it draws to a “dummy” surface and changing the UITIledImage code accordingly:
bool BaseRenderOSystem::startSpriteBatch(bool swap, int width, int height) { if (swap) { _swapped = true; _auxSurface = _renderSurface; _renderSurface = new Graphics::Surface(); _renderSurface->create(width, height, _auxSurface->format); } _spriteBatch = true; _batchNum = 1; return STATUS_OK; } bool BaseRenderOSystem::endSpriteBatch(bool swap) { if (_swapped) { _swapped = false; Graphics::Surface *temp; temp = _renderSurface; _renderSurface = _auxSurface; _auxSurface = temp; } _spriteBatch = false; _batchNum = 0; return STATUS_OK; } // .... bool UITiledImage::display(int x, int y, int width, int height) { int tileWidth = _middleMiddle.right - _middleMiddle.left; int tileHeight = _middleMiddle.bottom - _middleMiddle.top; int nuColumns = (width - (_middleLeft.right - _middleLeft.left) - (_middleRight.right - _middleRight.left)) / tileWidth; int nuRows = (height - (_upMiddle.bottom - _upMiddle.top) - (_downMiddle.bottom - _downMiddle.top)) / tileHeight; int col, row; assert (width != 0); assert (height != 0); // Below is the new if block, which is executed only once: if (_cache == nullptr || width != _width || height != _height) { // This as a side effect enables the special "dummy" rendering mode _gameRef->_renderer->startSpriteBatch(true, width, height); int x = 0; int y = 0; _width = width; _height = height; // top left/right _image->_surface->displayTrans(x, y, _upLeft); _image->_surface->displayTrans(x + (_upLeft.right - _upLeft.left) + nuColumns * tileWidth, y, _upRight); // Etc, same for SW, NW, NE tiles // tiles if (nuRows > 0 && nuColumns > 0) { yyy = y + (_upMiddle.bottom - _upMiddle.top); xxx = x + (_upLeft.right - _upLeft.left); _image->_surface->displayTrans(xxx, yyy, _middleMiddle); _image->_surface->repeatLastDisplayOp(tileWidth, tileWidth, nuColumns, nuRows); } _gameRef->_renderer->endSpriteBatch(false); // This is where we retrieve the complete image _cache = _gameRef->_renderer->getAuxSurface(); } Rect32 dst; dst.top = 0; dst.left = 0; dst.setWidth(width); dst.setHeight(height); _cache->displayTrans(x, y, dst); return STATUS_OK; }
Then we store that surface and when we need to redraw the UITiledImage we simply use the cached version: single drawing call, less overhead.