src/core/tile-cache.c (view raw)
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
10void 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->activePalette = 0;
16 memset(cache->globalPaletteVersion, 0, sizeof(cache->globalPaletteVersion));
17}
18
19static void _freeCache(struct mTileCache* cache) {
20 unsigned count0;
21 count0 = 1 << mTileCacheSystemInfoGetPalette0Count(cache->sysConfig);
22 unsigned count1;
23 count1 = 1 << mTileCacheSystemInfoGetPalette1Count(cache->sysConfig);
24 unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
25 unsigned size = count0 > count1 ? count0 : count1;
26 if (cache->cache) {
27 mappedMemoryFree(cache->cache, 8 * 8 * 2 * tiles * size);
28 cache->cache = NULL;
29 }
30 if (cache->status) {
31 mappedMemoryFree(cache->status, tiles * size * sizeof(*cache->status));
32 cache->status = NULL;
33 }
34 free(cache->globalPaletteVersion[0]);
35 free(cache->globalPaletteVersion[1]);
36 memset(cache->globalPaletteVersion, 0, sizeof(cache->globalPaletteVersion));
37}
38
39static void _redoCacheSize(struct mTileCache* cache) {
40 if (!mTileCacheConfigurationIsShouldStore(cache->config)) {
41 return;
42 }
43 unsigned count0 = mTileCacheSystemInfoGetPalette0Count(cache->sysConfig);
44 unsigned bpp0 = mTileCacheSystemInfoGetPalette0BPP(cache->sysConfig);
45 bpp0 = 1 << (1 << bpp0);
46 if (count0) {
47 count0 = 1 << count0;
48 }
49 unsigned count1 = mTileCacheSystemInfoGetPalette1Count(cache->sysConfig);
50 unsigned bpp1 = mTileCacheSystemInfoGetPalette1BPP(cache->sysConfig);
51 bpp1 = 1 << (1 << bpp1);
52 if (count1) {
53 count1 = 1 << count1;
54 }
55 unsigned size = count0 > count1 ? count0 : count1;
56 if (!size) {
57 return;
58 }
59 cache->entriesPerTile = size;
60 unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
61 cache->cache = anonymousMemoryMap(8 * 8 * 2 * tiles * size);
62 cache->status = anonymousMemoryMap(tiles * size * sizeof(*cache->status));
63 if (count0) {
64 cache->globalPaletteVersion[0] = malloc(count0 * bpp0 * sizeof(*cache->globalPaletteVersion[0]));
65 }
66 if (count1) {
67 cache->globalPaletteVersion[1] = malloc(count1 * bpp1 * sizeof(*cache->globalPaletteVersion[1]));
68 }
69}
70
71void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration config) {
72 _freeCache(cache);
73 cache->config = config;
74 _redoCacheSize(cache);
75}
76
77void mTileCacheConfigureSystem(struct mTileCache* cache, mTileCacheSystemInfo config) {
78 _freeCache(cache);
79 cache->sysConfig = config;
80 _redoCacheSize(cache);
81}
82
83void mTileCacheDeinit(struct mTileCache* cache) {
84 _freeCache(cache);
85}
86
87void mTileCacheWriteVRAM(struct mTileCache* cache, uint32_t address) {
88 unsigned bpp = cache->bpp + 3;
89 unsigned count = cache->entriesPerTile;
90 size_t i;
91 for (i = 0; i < count; ++i) {
92 cache->status[(address >> bpp) * count + i].vramClean = 0;
93 ++cache->status[(address >> bpp) * count + i].vramVersion;
94 }
95}
96
97void mTileCacheWritePalette(struct mTileCache* cache, uint32_t address) {
98 if (cache->globalPaletteVersion[0]) {
99 ++cache->globalPaletteVersion[0][address >> 1];
100 }
101 if (cache->globalPaletteVersion[1]) {
102 ++cache->globalPaletteVersion[1][address >> 1];
103 }
104}
105
106void mTileCacheSetPalette(struct mTileCache* cache, int palette) {
107 cache->activePalette = palette;
108 if (palette == 0) {
109 cache->bpp = mTileCacheSystemInfoGetPalette0BPP(cache->sysConfig);
110 cache->count = 1 << mTileCacheSystemInfoGetPalette0Count(cache->sysConfig);
111 } else {
112 cache->bpp = mTileCacheSystemInfoGetPalette1BPP(cache->sysConfig);
113 cache->count = 1 << mTileCacheSystemInfoGetPalette1Count(cache->sysConfig);
114 }
115 cache->entries = 1 << (1 << cache->bpp);
116}
117
118static void _regenerateTile4(struct mTileCache* cache, uint16_t* tile, unsigned tileId, unsigned paletteId) {
119 uint8_t* start = (uint8_t*) &cache->vram[tileId << 3];
120 paletteId <<= 2;
121 uint16_t* palette = &cache->palette[paletteId];
122 int i;
123 for (i = 0; i < 8; ++i) {
124 uint8_t tileDataLower = start[0];
125 uint8_t tileDataUpper = start[1];
126 start += 2;
127 int pixel;
128 pixel = ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
129 tile[0] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
130 pixel = ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
131 tile[1] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
132 pixel = ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
133 tile[2] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
134 pixel = ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
135 tile[3] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
136 pixel = ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
137 tile[4] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
138 pixel = ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
139 tile[5] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
140 pixel = (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
141 tile[6] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
142 pixel = ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
143 tile[7] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
144 tile += 8;
145 }
146}
147
148static void _regenerateTile16(struct mTileCache* cache, uint16_t* tile, unsigned tileId, unsigned paletteId) {
149 uint32_t* start = (uint32_t*) &cache->vram[tileId << 4];
150 paletteId <<= 4;
151 uint16_t* palette = &cache->palette[paletteId];
152 int i;
153 for (i = 0; i < 8; ++i) {
154 uint32_t line = *start;
155 ++start;
156 int pixel;
157 pixel = line & 0xF;
158 tile[0] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
159 pixel = (line >> 4) & 0xF;
160 tile[1] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
161 pixel = (line >> 8) & 0xF;
162 tile[2] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
163 pixel = (line >> 12) & 0xF;
164 tile[3] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
165 pixel = (line >> 16) & 0xF;
166 tile[4] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
167 pixel = (line >> 20) & 0xF;
168 tile[5] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
169 pixel = (line >> 24) & 0xF;
170 tile[6] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
171 pixel = (line >> 28) & 0xF;
172 tile[7] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
173 tile += 8;
174 }
175}
176
177static void _regenerateTile256(struct mTileCache* cache, uint16_t* tile, unsigned tileId, unsigned paletteId) {
178 uint32_t* start = (uint32_t*) &cache->vram[tileId << 5];
179 paletteId <<= 8;
180 uint16_t* palette = &cache->palette[paletteId];
181 int i;
182 for (i = 0; i < 8; ++i) {
183 uint32_t line = *start;
184 ++start;
185 int pixel;
186 pixel = line & 0xFF;
187 tile[0] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
188 pixel = (line >> 8) & 0xFF;
189 tile[1] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
190 pixel = (line >> 16) & 0xFF;
191 tile[2] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
192 pixel = (line >> 24) & 0xFF;
193 tile[3] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
194
195 line = *start;
196 ++start;
197 pixel = line & 0xFF;
198 tile[4] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
199 pixel = (line >> 8) & 0xFF;
200 tile[5] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
201 pixel = (line >> 16) & 0xFF;
202 tile[6] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
203 pixel = (line >> 24) & 0xFF;
204 tile[7] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
205 tile += 8;
206 }
207}
208
209static inline uint16_t* _tileLookup(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
210 if (mTileCacheConfigurationIsShouldStore(cache->config)) {
211 unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
212 return &cache->cache[(tileId + paletteId * tiles) << 6];
213 } else {
214 return cache->temporaryTile;
215 }
216}
217
218const uint16_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
219 unsigned cPaletteId = cache->activePalette;
220 unsigned count = cache->entriesPerTile;
221 unsigned bpp = cache->bpp;
222 struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
223 struct mTileCacheEntry desiredStatus = {
224 .paletteVersion = cache->globalPaletteVersion[cPaletteId][paletteId],
225 .vramVersion = status->vramVersion,
226 .vramClean = 1,
227 .paletteId = paletteId,
228 .activePalette = cPaletteId
229 };
230 uint16_t* tile = _tileLookup(cache, tileId, paletteId);
231 if (!mTileCacheConfigurationIsShouldStore(cache->config) || memcmp(status, &desiredStatus, sizeof(*status))) {
232 switch (bpp) {
233 case 0:
234 return NULL;
235 case 1:
236 _regenerateTile4(cache, tile, tileId, paletteId);
237 break;
238 case 2:
239 _regenerateTile16(cache, tile, tileId, paletteId);
240 break;
241 case 3:
242 _regenerateTile256(cache, tile, tileId, paletteId);
243 break;
244 }
245 *status = desiredStatus;
246 }
247 return tile;
248}
249
250const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId) {
251 unsigned cPaletteId = cache->activePalette;
252 unsigned count = cache->entriesPerTile;
253 unsigned bpp = cache->bpp;
254 struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
255 struct mTileCacheEntry desiredStatus = {
256 .paletteVersion = cache->globalPaletteVersion[cPaletteId][paletteId],
257 .vramVersion = status->vramVersion,
258 .vramClean = 1,
259 .paletteId = paletteId,
260 .activePalette = cPaletteId
261 };
262 uint16_t* tile = NULL;
263 if (memcmp(status, &desiredStatus, sizeof(*status))) {
264 tile = _tileLookup(cache, tileId, paletteId);
265 switch (bpp) {
266 case 0:
267 return NULL;
268 case 1:
269 _regenerateTile4(cache, tile, tileId, paletteId);
270 break;
271 case 2:
272 _regenerateTile16(cache, tile, tileId, paletteId);
273 break;
274 case 3:
275 _regenerateTile256(cache, tile, tileId, paletteId);
276 break;
277 }
278 *status = desiredStatus;
279 }
280 if (memcmp(status, &entry[paletteId], sizeof(*status))) {
281 tile = _tileLookup(cache, tileId, paletteId);
282 entry[paletteId] = *status;
283 }
284 return tile;
285}
286
287const uint8_t* mTileCacheGetRawTile(struct mTileCache* cache, unsigned tileId) {
288 unsigned bpp = cache->bpp;
289 switch (bpp) {
290 case 0:
291 return NULL;
292 default:
293 return (uint8_t*) &cache->vram[tileId << (2 + bpp)];
294 }
295}
296
297const uint16_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) {
298 unsigned bpp = cache->bpp;
299 switch (bpp) {
300 default:
301 return NULL;
302 case 1:
303 return &cache->palette[paletteId << 2];
304 case 2:
305 return &cache->palette[paletteId << 4];
306 case 3:
307 return &cache->palette[paletteId << 8];
308 }
309}