1 /* Copyright (c) 2013-2016 Jeffrey Pfau
2  *
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <mgba/core/tile-cache.h>
7 
8 #include <mgba-util/memory.h>
9 
mTileCacheInit(struct mTileCache * cache)10 void mTileCacheInit(struct mTileCache* cache) {
11 	// TODO: Reconfigurable cache for space savings
12 	cache->cache = NULL;
13 	cache->config = mTileCacheConfigurationFillShouldStore(0);
14 	cache->status = NULL;
15 	cache->globalPaletteVersion = NULL;
16 	cache->palette = NULL;
17 }
18 
_freeCache(struct mTileCache * cache)19 static void _freeCache(struct mTileCache* cache) {
20 	unsigned size = 1 << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig);
21 	unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
22 	if (cache->cache) {
23 		mappedMemoryFree(cache->cache, 8 * 8 * sizeof(color_t) * tiles * size);
24 		cache->cache = NULL;
25 	}
26 	if (cache->status) {
27 		mappedMemoryFree(cache->status, tiles * size * sizeof(*cache->status));
28 		cache->status = NULL;
29 	}
30 	free(cache->globalPaletteVersion);
31 	cache->globalPaletteVersion = NULL;
32 	free(cache->palette);
33 	cache->palette = NULL;
34 }
35 
_redoCacheSize(struct mTileCache * cache)36 static void _redoCacheSize(struct mTileCache* cache) {
37 	if (!mTileCacheConfigurationIsShouldStore(cache->config)) {
38 		return;
39 	}
40 	unsigned size = mTileCacheSystemInfoGetPaletteCount(cache->sysConfig);
41 	unsigned bpp = mTileCacheSystemInfoGetPaletteBPP(cache->sysConfig);
42 	cache->bpp = bpp;
43 	bpp = 1 << (1 << bpp);
44 	size = 1 << size;
45 	cache->entriesPerTile = size;
46 	unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
47 	cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles * size);
48 	cache->status = anonymousMemoryMap(tiles * size * sizeof(*cache->status));
49 	cache->globalPaletteVersion = calloc(size, sizeof(*cache->globalPaletteVersion));
50 	cache->palette = calloc(size * bpp, sizeof(*cache->palette));
51 }
52 
mTileCacheConfigure(struct mTileCache * cache,mTileCacheConfiguration config)53 void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration config) {
54 	if (cache->config == config) {
55 		return;
56 	}
57 	_freeCache(cache);
58 	cache->config = config;
59 	_redoCacheSize(cache);
60 }
61 
mTileCacheConfigureSystem(struct mTileCache * cache,mTileCacheSystemInfo config,uint32_t tileBase,uint32_t paletteBase)62 void mTileCacheConfigureSystem(struct mTileCache* cache, mTileCacheSystemInfo config, uint32_t tileBase, uint32_t paletteBase) {
63 	_freeCache(cache);
64 	cache->sysConfig = config;
65 	cache->tileBase = tileBase;
66 	cache->paletteBase = paletteBase;
67 	_redoCacheSize(cache);
68 }
69 
mTileCacheDeinit(struct mTileCache * cache)70 void mTileCacheDeinit(struct mTileCache* cache) {
71 	_freeCache(cache);
72 }
73 
mTileCacheWriteVRAM(struct mTileCache * cache,uint32_t address)74 void mTileCacheWriteVRAM(struct mTileCache* cache, uint32_t address) {
75 	if (address < cache->tileBase) {
76 		return;
77 	}
78 	address -= cache->tileBase;
79 	unsigned bpp = cache->bpp + 3;
80 	unsigned count = cache->entriesPerTile;
81 	address >>= bpp;
82 	if (address >= mTileCacheSystemInfoGetMaxTiles(cache->sysConfig)) {
83 		return;
84 	}
85 	size_t i;
86 	for (i = 0; i < count; ++i) {
87 		cache->status[address * count + i].vramClean = 0;
88 		++cache->status[address * count + i].vramVersion;
89 	}
90 }
91 
mTileCacheWritePalette(struct mTileCache * cache,uint32_t entry,color_t color)92 void mTileCacheWritePalette(struct mTileCache* cache, uint32_t entry, color_t color) {
93 	if (entry < cache->paletteBase) {
94 		return;
95 	}
96 	entry -= cache->paletteBase;
97 	unsigned maxEntry = (1 << (1 << cache->bpp)) * cache->entriesPerTile;
98 	if (entry >= maxEntry) {
99 		return;
100 	}
101 	cache->palette[entry] = color;
102 	entry >>= (1 << mTileCacheSystemInfoGetPaletteBPP(cache->sysConfig));
103 	++cache->globalPaletteVersion[entry];
104 }
105 
_regenerateTile4(struct mTileCache * cache,color_t * tile,unsigned tileId,unsigned paletteId)106 static void _regenerateTile4(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) {
107 	uint8_t* start = (uint8_t*) &cache->vram[tileId << 3];
108 	paletteId <<= 2;
109 	color_t* palette = &cache->palette[paletteId];
110 	int i;
111 	for (i = 0; i < 8; ++i) {
112 		uint8_t tileDataLower = start[0];
113 		uint8_t tileDataUpper = start[1];
114 		start += 2;
115 		int pixel;
116 		pixel = ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
117 		tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
118 		pixel = ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
119 		tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
120 		pixel = ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
121 		tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
122 		pixel = ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
123 		tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
124 		pixel = ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
125 		tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
126 		pixel = ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
127 		tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
128 		pixel = (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
129 		tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
130 		pixel = ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
131 		tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
132 		tile += 8;
133 	}
134 }
135 
_regenerateTile16(struct mTileCache * cache,color_t * tile,unsigned tileId,unsigned paletteId)136 static void _regenerateTile16(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) {
137 	uint32_t* start = (uint32_t*) &cache->vram[tileId << 4];
138 	paletteId <<= 4;
139 	color_t* palette = &cache->palette[paletteId];
140 	int i;
141 	for (i = 0; i < 8; ++i) {
142 		uint32_t line = *start;
143 		++start;
144 		int pixel;
145 		pixel = line & 0xF;
146 		tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
147 		pixel = (line >> 4) & 0xF;
148 		tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
149 		pixel = (line >> 8) & 0xF;
150 		tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
151 		pixel = (line >> 12) & 0xF;
152 		tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
153 		pixel = (line >> 16) & 0xF;
154 		tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
155 		pixel = (line >> 20) & 0xF;
156 		tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
157 		pixel = (line >> 24) & 0xF;
158 		tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
159 		pixel = (line >> 28) & 0xF;
160 		tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
161 		tile += 8;
162 	}
163 }
164 
_regenerateTile256(struct mTileCache * cache,color_t * tile,unsigned tileId,unsigned paletteId)165 static void _regenerateTile256(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) {
166 	uint32_t* start = (uint32_t*) &cache->vram[tileId << 5];
167 	paletteId <<= 8;
168 	color_t* palette = &cache->palette[paletteId];
169 	int i;
170 	for (i = 0; i < 8; ++i) {
171 		uint32_t line = *start;
172 		++start;
173 		int pixel;
174 		pixel = line & 0xFF;
175 		tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
176 		pixel = (line >> 8) & 0xFF;
177 		tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
178 		pixel = (line >> 16) & 0xFF;
179 		tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
180 		pixel = (line >> 24) & 0xFF;
181 		tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
182 
183 		line = *start;
184 		++start;
185 		pixel = line & 0xFF;
186 		tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
187 		pixel = (line >> 8) & 0xFF;
188 		tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
189 		pixel = (line >> 16) & 0xFF;
190 		tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
191 		pixel = (line >> 24) & 0xFF;
192 		tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
193 		tile += 8;
194 	}
195 }
196 
_tileLookup(struct mTileCache * cache,unsigned tileId,unsigned paletteId)197 static inline color_t* _tileLookup(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
198 	if (mTileCacheConfigurationIsShouldStore(cache->config)) {
199 		unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
200 #ifndef NDEBUG
201 		if (tileId >= tiles) {
202 			abort();
203 		}
204 		if (paletteId >= 1U << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig)) {
205 			abort();
206 		}
207 #endif
208 		return &cache->cache[(tileId + paletteId * tiles) << 6];
209 	} else {
210 		return cache->temporaryTile;
211 	}
212 }
213 
mTileCacheGetTile(struct mTileCache * cache,unsigned tileId,unsigned paletteId)214 const color_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
215 	unsigned count = cache->entriesPerTile;
216 	unsigned bpp = cache->bpp;
217 	struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
218 	struct mTileCacheEntry desiredStatus = {
219 		.paletteVersion = cache->globalPaletteVersion[paletteId],
220 		.vramVersion = status->vramVersion,
221 		.vramClean = 1,
222 		.paletteId = paletteId
223 	};
224 	color_t* tile = _tileLookup(cache, tileId, paletteId);
225 	if (!mTileCacheConfigurationIsShouldStore(cache->config) || memcmp(status, &desiredStatus, sizeof(*status))) {
226 		switch (bpp) {
227 		case 0:
228 			return NULL;
229 		case 1:
230 			_regenerateTile4(cache, tile, tileId, paletteId);
231 			break;
232 		case 2:
233 			_regenerateTile16(cache, tile, tileId, paletteId);
234 			break;
235 		case 3:
236 			_regenerateTile256(cache, tile, tileId, paletteId);
237 			break;
238 		}
239 		*status = desiredStatus;
240 	}
241 	return tile;
242 }
243 
mTileCacheGetTileIfDirty(struct mTileCache * cache,struct mTileCacheEntry * entry,unsigned tileId,unsigned paletteId)244 const color_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId) {
245 	unsigned count = cache->entriesPerTile;
246 	unsigned bpp = cache->bpp;
247 	struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
248 	struct mTileCacheEntry desiredStatus = {
249 		.paletteVersion = cache->globalPaletteVersion[paletteId],
250 		.vramVersion = status->vramVersion,
251 		.vramClean = 1,
252 		.paletteId = paletteId
253 	};
254 	color_t* tile = NULL;
255 	if (memcmp(status, &desiredStatus, sizeof(*status))) {
256 		tile = _tileLookup(cache, tileId, paletteId);
257 		switch (bpp) {
258 		case 0:
259 			return NULL;
260 		case 1:
261 			_regenerateTile4(cache, tile, tileId, paletteId);
262 			break;
263 		case 2:
264 			_regenerateTile16(cache, tile, tileId, paletteId);
265 			break;
266 		case 3:
267 			_regenerateTile256(cache, tile, tileId, paletteId);
268 			break;
269 		}
270 		*status = desiredStatus;
271 	}
272 	if (memcmp(status, &entry[paletteId], sizeof(*status))) {
273 		tile = _tileLookup(cache, tileId, paletteId);
274 		entry[paletteId] = *status;
275 	}
276 	return tile;
277 }
278 
mTileCacheGetPalette(struct mTileCache * cache,unsigned paletteId)279 const color_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) {
280 	return &cache->palette[paletteId << (1 << cache->bpp)];
281 }
282 
mTileCacheGetVRAM(struct mTileCache * cache,unsigned tileId)283 const uint16_t* mTileCacheGetVRAM(struct mTileCache* cache, unsigned tileId) {
284 	unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
285 	if (tileId >= tiles) {
286 		return NULL;
287 	}
288 	return &cache->vram[tileId << (cache->bpp + 2)];
289 }
290