Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tilebuffer may benefit from a cache #171

Open
timfel opened this issue Nov 24, 2022 · 1 comment
Open

Tilebuffer may benefit from a cache #171

timfel opened this issue Nov 24, 2022 · 1 comment

Comments

@timfel
Copy link
Contributor

timfel commented Nov 24, 2022

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
@tehKaiN
Copy link
Member

tehKaiN commented Nov 24, 2022

Thanks! I'll try to take a closer look at it during weekend, clean it up, and add an opt-in mechanism in case of ppl doing some unsafe stuff in tile draw callbacks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants