Tiled Surfaces

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:

They are heavy.

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.