Skip to content

Tilebuffer may benefit from a cache #171

@timfel

Description

@timfel

When some tiles repeat a lot and no other drawing is taking place (i.e, in the tile draw callback), we can cache what tiles were already drawn where and avoid starting the blitter in many cases.

A simple patch that I'm using right now attached:

diff --git a/include/ace/managers/viewport/tilebuffer.h b/include/ace/managers/viewport/tilebuffer.h
index 46156b3..5144b45 100644
--- a/include/ace/managers/viewport/tilebuffer.h
+++ b/include/ace/managers/viewport/tilebuffer.h
@@ -123,6 +123,7 @@ typedef struct _tTileBufferManager {
 	                              ///  TODO: refresh when scrollbuffer changes
 	tTileDrawCallback cbTileDraw; ///< Called when tile is redrawn
 	UBYTE **pTileData;            ///< 2D array of tile indices
+	UBYTE **pTileCache;           ///< 2D array of drawn tile indices
 	tBitMap *pTileSet;            ///< Tileset - one tile beneath another
 	// Margin & queue geometry
 	UBYTE ubMarginXLength; ///< Tile number in margins: left & right
diff --git a/src/ace/managers/viewport/tilebuffer.c b/src/ace/managers/viewport/tilebuffer.c
index 7f5ca1e..928a400 100644
--- a/src/ace/managers/viewport/tilebuffer.c
+++ b/src/ace/managers/viewport/tilebuffer.c
@@ -243,6 +243,15 @@ void tileBufferReset(
 		);
 	}
 
+	// Free old tile cache
+	if(pManager->pTileCache) {	
+		for(UWORD uwCol = pManager->uwMarginedWidth >> ubTileShift; uwCol--;) {
+			memFree(pManager->pTileCache[uwCol], (pManager->uwMarginedHeight >> ubTileShift) * sizeof(UBYTE) * 2);
+		}
+		memFree(pManager->pTileCache, (pManager->uwMarginedWidth >> ubTileShift) * sizeof(UBYTE *) * 2);
+		pManager->pTileCache = 0;
+	}
+
 	// Scrollin on one of dirs may be disabled - less redraw on other axis margin
 	pManager->uwMarginedWidth = bitmapGetByteWidth(pManager->pScroll->pFront)*8;
 	pManager->uwMarginedHeight = pManager->pScroll->uwBmAvailHeight;
@@ -260,6 +269,15 @@ void tileBufferReset(
 		pManager->ubMarginXLength, pManager->ubMarginYLength
 	);
 
+	// Init new tile cache
+	// TODO: like the redraw states, we always maintain a double buffered cache right now, which eats quite some memory
+	if((pManager->uwMarginedWidth >> ubTileShift) && (pManager->uwMarginedHeight >> ubTileShift)) {
+		pManager->pTileCache = memAllocFast((pManager->uwMarginedWidth >> ubTileShift) * sizeof(UBYTE*) * 2);
+		for(UWORD uwCol = (pManager->uwMarginedWidth >> ubTileShift); uwCol--;) {
+			pManager->pTileCache[uwCol] = memAllocFastClear((pManager->uwMarginedHeight >> ubTileShift) * sizeof(UBYTE) * 2);
+		}
+	}
+
 	// Reset margin redraw structs
 	tileBufferResetRedrawState(&pManager->pRedrawStates[0]);
 	tileBufferResetRedrawState(&pManager->pRedrawStates[1]);
@@ -445,6 +463,8 @@ void tileBufferRedrawAll(tTileBufferManager *pManager) {
 		UWORD uwTileOffsX = (wStartX << ubTileShift);
 
 		for (UWORD uwTileX = wStartX; uwTileX < uwEndX; ++uwTileX) {
+			// force redraw by invalidating cache
+			pManager->pTileCache[uwTileOffsX >> ubTileShift][uwTileOffsY >> ubTileShift] = pManager->pTileData[uwTileX][uwTileY] + 1;
 			tileBufferDrawTileQuick(
 				pManager, uwTileX, uwTileY, uwTileOffsX, uwTileOffsY
 			);
@@ -487,14 +507,22 @@ void tileBufferDrawTileQuick(
 	const tTileBufferManager *pManager, UWORD uwTileX, UWORD uwTileY,
 	UWORD uwBfrX, UWORD uwBfrY
 ) {
-	// This can't use safe blit fn because when scrolling in X direction,
-	// we need to draw on bitplane 1 as if it is part of bitplane 0.
-	blitUnsafeCopyAligned(
-		pManager->pTileSet,
-		0, pManager->pTileData[uwTileX][uwTileY] << pManager->ubTileShift,
-		pManager->pScroll->pBack, uwBfrX, uwBfrY,
-		pManager->ubTileSize, pManager->ubTileSize
-	);
+	UBYTE ubTileToDraw = pManager->pTileData[uwTileX][uwTileY];
+	UBYTE ubTileShift = pManager->ubTileShift;
+	UBYTE ubStateIdx = pManager->ubStateIdx;
+	UBYTE *pubDrawnTile = &pManager->pTileCache[uwBfrX >> (ubTileShift - ubStateIdx)][uwBfrY >> (ubTileShift - ubStateIdx)];
+	if (*pubDrawnTile != ubTileToDraw) {
+		// the correct tile is not already on the buffer
+		*pubDrawnTile = ubTileToDraw;
+		// This can't use safe blit fn because when scrolling in X direction,
+		// we need to draw on bitplane 1 as if it is part of bitplane 0.
+		blitUnsafeCopyAligned(
+			pManager->pTileSet,
+			0, ubTileToDraw << pManager->ubTileShift,
+			pManager->pScroll->pBack, uwBfrX, uwBfrY,
+			pManager->ubTileSize, pManager->ubTileSize
+		);
+	}
 	if(pManager->cbTileDraw) {
 		pManager->cbTileDraw(
 			uwTileX, uwTileY, pManager->pScroll->pBack, uwBfrX, uwBfrY

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    To do

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions