all repos — mgba @ 6900d130ae1fc082ac98c53213a4d31b1f9928fb

mGBA Game Boy Advance Emulator

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 >= 1U << 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}