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 = malloc(size * sizeof(*cache->globalPaletteVersion));
50 cache->palette = malloc(size * bpp * sizeof(*cache->palette));
51}
52
53void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration config) {
54 _freeCache(cache);
55 cache->config = config;
56 _redoCacheSize(cache);
57}
58
59void mTileCacheConfigureSystem(struct mTileCache* cache, mTileCacheSystemInfo config, uint32_t tileBase, uint32_t paletteBase) {
60 _freeCache(cache);
61 cache->sysConfig = config;
62 cache->tileBase = tileBase;
63 cache->paletteBase = paletteBase;
64 _redoCacheSize(cache);
65}
66
67void mTileCacheDeinit(struct mTileCache* cache) {
68 _freeCache(cache);
69}
70
71void mTileCacheWriteVRAM(struct mTileCache* cache, uint32_t address) {
72 if (address < cache->tileBase) {
73 return;
74 }
75 address -= cache->tileBase;
76 unsigned bpp = cache->bpp + 3;
77 unsigned count = cache->entriesPerTile;
78 address >>= bpp;
79 if (address >= mTileCacheSystemInfoGetMaxTiles(cache->sysConfig)) {
80 return;
81 }
82 size_t i;
83 for (i = 0; i < count; ++i) {
84 cache->status[address * count + i].vramClean = 0;
85 ++cache->status[address * count + i].vramVersion;
86 }
87}
88
89void mTileCacheWritePalette(struct mTileCache* cache, uint32_t entry, color_t color) {
90 if (entry < cache->paletteBase) {
91 return;
92 }
93 entry -= cache->paletteBase;
94 unsigned maxEntry = (1 << (1 << cache->bpp)) * cache->entriesPerTile;
95 if (entry >= maxEntry) {
96 return;
97 }
98 cache->palette[entry] = color;
99 entry >>= (1 << mTileCacheSystemInfoGetPaletteBPP(cache->sysConfig));
100 ++cache->globalPaletteVersion[entry];
101}
102
103static void _regenerateTile4(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) {
104 uint8_t* start = (uint8_t*) &cache->vram[tileId << 3];
105 paletteId <<= 2;
106 color_t* palette = &cache->palette[paletteId];
107 int i;
108 for (i = 0; i < 8; ++i) {
109 uint8_t tileDataLower = start[0];
110 uint8_t tileDataUpper = start[1];
111 start += 2;
112 int pixel;
113 pixel = ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
114 tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
115 pixel = ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
116 tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
117 pixel = ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
118 tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
119 pixel = ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
120 tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
121 pixel = ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
122 tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
123 pixel = ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
124 tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
125 pixel = (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
126 tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
127 pixel = ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
128 tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
129 tile += 8;
130 }
131}
132
133static void _regenerateTile16(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) {
134 uint32_t* start = (uint32_t*) &cache->vram[tileId << 4];
135 paletteId <<= 4;
136 color_t* palette = &cache->palette[paletteId];
137 int i;
138 for (i = 0; i < 8; ++i) {
139 uint32_t line = *start;
140 ++start;
141 int pixel;
142 pixel = line & 0xF;
143 tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
144 pixel = (line >> 4) & 0xF;
145 tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
146 pixel = (line >> 8) & 0xF;
147 tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
148 pixel = (line >> 12) & 0xF;
149 tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
150 pixel = (line >> 16) & 0xF;
151 tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
152 pixel = (line >> 20) & 0xF;
153 tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
154 pixel = (line >> 24) & 0xF;
155 tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
156 pixel = (line >> 28) & 0xF;
157 tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
158 tile += 8;
159 }
160}
161
162static void _regenerateTile256(struct mTileCache* cache, color_t* tile, unsigned tileId, unsigned paletteId) {
163 uint32_t* start = (uint32_t*) &cache->vram[tileId << 5];
164 paletteId <<= 8;
165 color_t* palette = &cache->palette[paletteId];
166 int i;
167 for (i = 0; i < 8; ++i) {
168 uint32_t line = *start;
169 ++start;
170 int pixel;
171 pixel = line & 0xFF;
172 tile[0] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
173 pixel = (line >> 8) & 0xFF;
174 tile[1] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
175 pixel = (line >> 16) & 0xFF;
176 tile[2] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
177 pixel = (line >> 24) & 0xFF;
178 tile[3] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
179
180 line = *start;
181 ++start;
182 pixel = line & 0xFF;
183 tile[4] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
184 pixel = (line >> 8) & 0xFF;
185 tile[5] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
186 pixel = (line >> 16) & 0xFF;
187 tile[6] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
188 pixel = (line >> 24) & 0xFF;
189 tile[7] = pixel ? palette[pixel] | 0xFF000000 : palette[pixel];
190 tile += 8;
191 }
192}
193
194static inline color_t* _tileLookup(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
195 if (mTileCacheConfigurationIsShouldStore(cache->config)) {
196 unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
197#ifndef NDEBUG
198 if (tileId >= tiles) {
199 abort();
200 }
201 if (paletteId >= 1 << mTileCacheSystemInfoGetPaletteCount(cache->sysConfig)) {
202 abort();
203 }
204#endif
205 return &cache->cache[(tileId + paletteId * tiles) << 6];
206 } else {
207 return cache->temporaryTile;
208 }
209}
210
211const color_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
212 unsigned count = cache->entriesPerTile;
213 unsigned bpp = cache->bpp;
214 struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
215 struct mTileCacheEntry desiredStatus = {
216 .paletteVersion = cache->globalPaletteVersion[paletteId],
217 .vramVersion = status->vramVersion,
218 .vramClean = 1,
219 .paletteId = paletteId
220 };
221 color_t* tile = _tileLookup(cache, tileId, paletteId);
222 if (!mTileCacheConfigurationIsShouldStore(cache->config) || memcmp(status, &desiredStatus, sizeof(*status))) {
223 switch (bpp) {
224 case 0:
225 return NULL;
226 case 1:
227 _regenerateTile4(cache, tile, tileId, paletteId);
228 break;
229 case 2:
230 _regenerateTile16(cache, tile, tileId, paletteId);
231 break;
232 case 3:
233 _regenerateTile256(cache, tile, tileId, paletteId);
234 break;
235 }
236 *status = desiredStatus;
237 }
238 return tile;
239}
240
241const color_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId) {
242 unsigned count = cache->entriesPerTile;
243 unsigned bpp = cache->bpp;
244 struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
245 struct mTileCacheEntry desiredStatus = {
246 .paletteVersion = cache->globalPaletteVersion[paletteId],
247 .vramVersion = status->vramVersion,
248 .vramClean = 1,
249 .paletteId = paletteId
250 };
251 color_t* tile = NULL;
252 if (memcmp(status, &desiredStatus, sizeof(*status))) {
253 tile = _tileLookup(cache, tileId, paletteId);
254 switch (bpp) {
255 case 0:
256 return NULL;
257 case 1:
258 _regenerateTile4(cache, tile, tileId, paletteId);
259 break;
260 case 2:
261 _regenerateTile16(cache, tile, tileId, paletteId);
262 break;
263 case 3:
264 _regenerateTile256(cache, tile, tileId, paletteId);
265 break;
266 }
267 *status = desiredStatus;
268 }
269 if (memcmp(status, &entry[paletteId], sizeof(*status))) {
270 tile = _tileLookup(cache, tileId, paletteId);
271 entry[paletteId] = *status;
272 }
273 return tile;
274}
275
276const color_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) {
277 return &cache->palette[paletteId << (1 << cache->bpp)];
278}
279
280const uint16_t* mTileCacheGetVRAM(struct mTileCache* cache, unsigned tileId) {
281 return &cache->vram[tileId << (cache->bpp + 2)];
282}