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 return &cache->cache[(tileId + paletteId * tiles) << 6];
198 } else {
199 return cache->temporaryTile;
200 }
201}
202
203const color_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
204 unsigned count = cache->entriesPerTile;
205 unsigned bpp = cache->bpp;
206 struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
207 struct mTileCacheEntry desiredStatus = {
208 .paletteVersion = cache->globalPaletteVersion[paletteId],
209 .vramVersion = status->vramVersion,
210 .vramClean = 1,
211 .paletteId = paletteId
212 };
213 color_t* tile = _tileLookup(cache, tileId, paletteId);
214 if (!mTileCacheConfigurationIsShouldStore(cache->config) || memcmp(status, &desiredStatus, sizeof(*status))) {
215 switch (bpp) {
216 case 0:
217 return NULL;
218 case 1:
219 _regenerateTile4(cache, tile, tileId, paletteId);
220 break;
221 case 2:
222 _regenerateTile16(cache, tile, tileId, paletteId);
223 break;
224 case 3:
225 _regenerateTile256(cache, tile, tileId, paletteId);
226 break;
227 }
228 *status = desiredStatus;
229 }
230 return tile;
231}
232
233const color_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId) {
234 unsigned count = cache->entriesPerTile;
235 unsigned bpp = cache->bpp;
236 struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
237 struct mTileCacheEntry desiredStatus = {
238 .paletteVersion = cache->globalPaletteVersion[paletteId],
239 .vramVersion = status->vramVersion,
240 .vramClean = 1,
241 .paletteId = paletteId
242 };
243 color_t* tile = NULL;
244 if (memcmp(status, &desiredStatus, sizeof(*status))) {
245 tile = _tileLookup(cache, tileId, paletteId);
246 switch (bpp) {
247 case 0:
248 return NULL;
249 case 1:
250 _regenerateTile4(cache, tile, tileId, paletteId);
251 break;
252 case 2:
253 _regenerateTile16(cache, tile, tileId, paletteId);
254 break;
255 case 3:
256 _regenerateTile256(cache, tile, tileId, paletteId);
257 break;
258 }
259 *status = desiredStatus;
260 }
261 if (memcmp(status, &entry[paletteId], sizeof(*status))) {
262 tile = _tileLookup(cache, tileId, paletteId);
263 entry[paletteId] = *status;
264 }
265 return tile;
266}
267
268const color_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) {
269 return &cache->palette[paletteId << (1 << cache->bpp)];
270}
271
272const uint16_t* mTileCacheGetVRAM(struct mTileCache* cache, unsigned tileId) {
273 return &cache->vram[tileId << (cache->bpp + 2)];
274}