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->globalPaletteVersion = NULL;
16 cache->palette = NULL;
17}
18
19static 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
36static 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
53void 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
62void 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
70void mTileCacheDeinit(struct mTileCache* cache) {
71 _freeCache(cache);
72}
73
74void 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
92void 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
106static 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
136static 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
165static 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
197static 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
214const 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
244const 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
279const color_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) {
280 return &cache->palette[paletteId << (1 << cache->bpp)];
281}
282
283const 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}