all repos — mgba @ 57530a32b471efbfd1dbbc89c22a93119e12f55a

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