all repos — mgba @ a8f29906140feba11bdd4cd88574aee275f6f096

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->activePalette = 0;
 16	memset(cache->globalPaletteVersion, 0, sizeof(cache->globalPaletteVersion));
 17}
 18
 19static void _freeCache(struct mTileCache* cache) {
 20	unsigned count0;
 21	count0 = 1 << mTileCacheSystemInfoGetPalette0Count(cache->sysConfig);
 22	unsigned count1;
 23	count1 = 1 << mTileCacheSystemInfoGetPalette1Count(cache->sysConfig);
 24	unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
 25	unsigned size = count0 > count1 ? count0 : count1;
 26	if (cache->cache) {
 27		mappedMemoryFree(cache->cache, 8 * 8 * 2 * tiles * size);
 28		cache->cache = NULL;
 29	}
 30	if (cache->status) {
 31		mappedMemoryFree(cache->status, tiles * size * sizeof(*cache->status));
 32		cache->status = NULL;
 33	}
 34	free(cache->globalPaletteVersion[0]);
 35	free(cache->globalPaletteVersion[1]);
 36	memset(cache->globalPaletteVersion, 0, sizeof(cache->globalPaletteVersion));
 37}
 38
 39static void _redoCacheSize(struct mTileCache* cache) {
 40	if (!mTileCacheConfigurationIsShouldStore(cache->config)) {
 41		return;
 42	}
 43	unsigned count0 = mTileCacheSystemInfoGetPalette0Count(cache->sysConfig);
 44	unsigned bpp0 = mTileCacheSystemInfoGetPalette0BPP(cache->sysConfig);
 45	bpp0 = 1 << (1 << bpp0);
 46	if (count0) {
 47		count0 = 1 << count0;
 48	}
 49	unsigned count1 = mTileCacheSystemInfoGetPalette1Count(cache->sysConfig);
 50	unsigned bpp1 = mTileCacheSystemInfoGetPalette1BPP(cache->sysConfig);
 51	bpp1 = 1 << (1 << bpp1);
 52	if (count1) {
 53		count1 = 1 << count1;
 54	}
 55	unsigned size = count0 > count1 ? count0 : count1;
 56	if (!size) {
 57		return;
 58	}
 59	cache->entriesPerTile = size;
 60	unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
 61	cache->cache = anonymousMemoryMap(8 * 8 * 2 * tiles * size);
 62	cache->status = anonymousMemoryMap(tiles * size * sizeof(*cache->status));
 63	if (count0) {
 64		cache->globalPaletteVersion[0] = malloc(count0 * bpp0 * sizeof(*cache->globalPaletteVersion[0]));
 65	}
 66	if (count1) {
 67		cache->globalPaletteVersion[1] = malloc(count1 * bpp1 * sizeof(*cache->globalPaletteVersion[1]));
 68	}
 69}
 70
 71void mTileCacheConfigure(struct mTileCache* cache, mTileCacheConfiguration config) {
 72	_freeCache(cache);
 73	cache->config = config;
 74	_redoCacheSize(cache);
 75}
 76
 77void mTileCacheConfigureSystem(struct mTileCache* cache, mTileCacheSystemInfo config) {
 78	_freeCache(cache);
 79	cache->sysConfig = config;
 80	_redoCacheSize(cache);
 81}
 82
 83void mTileCacheDeinit(struct mTileCache* cache) {
 84	_freeCache(cache);
 85}
 86
 87void mTileCacheWriteVRAM(struct mTileCache* cache, uint32_t address) {
 88	unsigned bpp = cache->bpp + 3;
 89	unsigned count = cache->entriesPerTile;
 90	size_t i;
 91	for (i = 0; i < count; ++i) {
 92		cache->status[(address >> bpp) * count + i].vramClean = 0;
 93		++cache->status[(address >> bpp) * count + i].vramVersion;
 94	}
 95}
 96
 97void mTileCacheWritePalette(struct mTileCache* cache, uint32_t address) {
 98	if (cache->globalPaletteVersion[0]) {
 99		++cache->globalPaletteVersion[0][address >> 1];
100	}
101	if (cache->globalPaletteVersion[1]) {
102		++cache->globalPaletteVersion[1][address >> 1];
103	}
104}
105
106void mTileCacheSetPalette(struct mTileCache* cache, int palette) {
107	cache->activePalette = palette;
108	if (palette == 0) {
109		cache->bpp = mTileCacheSystemInfoGetPalette0BPP(cache->sysConfig);
110		cache->count = 1 << mTileCacheSystemInfoGetPalette0Count(cache->sysConfig);
111	} else {
112		cache->bpp = mTileCacheSystemInfoGetPalette1BPP(cache->sysConfig);
113		cache->count = 1 << mTileCacheSystemInfoGetPalette1Count(cache->sysConfig);
114	}
115	cache->entries = 1 << (1 << cache->bpp);
116}
117
118static void _regenerateTile4(struct mTileCache* cache, uint16_t* tile, unsigned tileId, unsigned paletteId) {
119	uint8_t* start = (uint8_t*) &cache->vram[tileId << 3];
120	paletteId <<= 2;
121	uint16_t* palette = &cache->palette[paletteId];
122	int i;
123	for (i = 0; i < 8; ++i) {
124		uint8_t tileDataLower = start[0];
125		uint8_t tileDataUpper = start[1];
126		start += 2;
127		int pixel;
128		pixel = ((tileDataUpper & 128) >> 6) | ((tileDataLower & 128) >> 7);
129		tile[0] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
130		pixel = ((tileDataUpper & 64) >> 5) | ((tileDataLower & 64) >> 6);
131		tile[1] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
132		pixel = ((tileDataUpper & 32) >> 4) | ((tileDataLower & 32) >> 5);
133		tile[2] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
134		pixel = ((tileDataUpper & 16) >> 3) | ((tileDataLower & 16) >> 4);
135		tile[3] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
136		pixel = ((tileDataUpper & 8) >> 2) | ((tileDataLower & 8) >> 3);
137		tile[4] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
138		pixel = ((tileDataUpper & 4) >> 1) | ((tileDataLower & 4) >> 2);
139		tile[5] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
140		pixel = (tileDataUpper & 2) | ((tileDataLower & 2) >> 1);
141		tile[6] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
142		pixel = ((tileDataUpper & 1) << 1) | (tileDataLower & 1);
143		tile[7] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
144		tile += 8;
145	}
146}
147
148static void _regenerateTile16(struct mTileCache* cache, uint16_t* tile, unsigned tileId, unsigned paletteId) {
149	uint32_t* start = (uint32_t*) &cache->vram[tileId << 4];
150	paletteId <<= 4;
151	uint16_t* palette = &cache->palette[paletteId];
152	int i;
153	for (i = 0; i < 8; ++i) {
154		uint32_t line = *start;
155		++start;
156		int pixel;
157		pixel = line & 0xF;
158		tile[0] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
159		pixel = (line >> 4) & 0xF;
160		tile[1] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
161		pixel = (line >> 8) & 0xF;
162		tile[2] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
163		pixel = (line >> 12) & 0xF;
164		tile[3] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
165		pixel = (line >> 16) & 0xF;
166		tile[4] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
167		pixel = (line >> 20) & 0xF;
168		tile[5] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
169		pixel = (line >> 24) & 0xF;
170		tile[6] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
171		pixel = (line >> 28) & 0xF;
172		tile[7] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
173		tile += 8;
174	}
175}
176
177static void _regenerateTile256(struct mTileCache* cache, uint16_t* tile, unsigned tileId, unsigned paletteId) {
178	uint32_t* start = (uint32_t*) &cache->vram[tileId << 5];
179	paletteId <<= 8;
180	uint16_t* palette = &cache->palette[paletteId];
181	int i;
182	for (i = 0; i < 8; ++i) {
183		uint32_t line = *start;
184		++start;
185		int pixel;
186		pixel = line & 0xFF;
187		tile[0] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
188		pixel = (line >> 8) & 0xFF;
189		tile[1] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
190		pixel = (line >> 16) & 0xFF;
191		tile[2] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
192		pixel = (line >> 24) & 0xFF;
193		tile[3] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
194
195		line = *start;
196		++start;
197		pixel = line & 0xFF;
198		tile[4] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
199		pixel = (line >> 8) & 0xFF;
200		tile[5] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
201		pixel = (line >> 16) & 0xFF;
202		tile[6] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
203		pixel = (line >> 24) & 0xFF;
204		tile[7] = pixel ? palette[pixel] | 0x8000 : palette[pixel] & 0x7FFF;
205		tile += 8;
206	}
207}
208
209static inline uint16_t* _tileLookup(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
210	if (mTileCacheConfigurationIsShouldStore(cache->config)) {
211		unsigned tiles = mTileCacheSystemInfoGetMaxTiles(cache->sysConfig);
212		return &cache->cache[(tileId + paletteId * tiles) << 6];
213	} else {
214		return cache->temporaryTile;
215	}
216}
217
218const uint16_t* mTileCacheGetTile(struct mTileCache* cache, unsigned tileId, unsigned paletteId) {
219	unsigned cPaletteId = cache->activePalette;
220	unsigned count = cache->entriesPerTile;
221	unsigned bpp = cache->bpp;
222	struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
223	struct mTileCacheEntry desiredStatus = {
224		.paletteVersion = cache->globalPaletteVersion[cPaletteId][paletteId],
225		.vramVersion = status->vramVersion,
226		.vramClean = 1,
227		.paletteId = paletteId,
228		.activePalette = cPaletteId
229	};
230	uint16_t* tile = _tileLookup(cache, tileId, paletteId);
231	if (!mTileCacheConfigurationIsShouldStore(cache->config) || memcmp(status, &desiredStatus, sizeof(*status))) {
232		switch (bpp) {
233		case 0:
234			return NULL;
235		case 1:
236			_regenerateTile4(cache, tile, tileId, paletteId);
237			break;
238		case 2:
239			_regenerateTile16(cache, tile, tileId, paletteId);
240			break;
241		case 3:
242			_regenerateTile256(cache, tile, tileId, paletteId);
243			break;
244		}
245		*status = desiredStatus;
246	}
247	return tile;
248}
249
250const uint16_t* mTileCacheGetTileIfDirty(struct mTileCache* cache, struct mTileCacheEntry* entry, unsigned tileId, unsigned paletteId) {
251	unsigned cPaletteId = cache->activePalette;
252	unsigned count = cache->entriesPerTile;
253	unsigned bpp = cache->bpp;
254	struct mTileCacheEntry* status = &cache->status[tileId * count + paletteId];
255	struct mTileCacheEntry desiredStatus = {
256		.paletteVersion = cache->globalPaletteVersion[cPaletteId][paletteId],
257		.vramVersion = status->vramVersion,
258		.vramClean = 1,
259		.paletteId = paletteId,
260		.activePalette = cPaletteId
261	};
262	uint16_t* tile = NULL;
263	if (memcmp(status, &desiredStatus, sizeof(*status))) {
264		tile = _tileLookup(cache, tileId, paletteId);
265		switch (bpp) {
266		case 0:
267			return NULL;
268		case 1:
269			_regenerateTile4(cache, tile, tileId, paletteId);
270			break;
271		case 2:
272			_regenerateTile16(cache, tile, tileId, paletteId);
273			break;
274		case 3:
275			_regenerateTile256(cache, tile, tileId, paletteId);
276			break;
277		}
278		*status = desiredStatus;
279	}
280	if (memcmp(status, &entry[paletteId], sizeof(*status))) {
281		tile = _tileLookup(cache, tileId, paletteId);
282		entry[paletteId] = *status;
283	}
284	return tile;
285}
286
287const uint8_t* mTileCacheGetRawTile(struct mTileCache* cache, unsigned tileId) {
288	unsigned bpp = cache->bpp;
289	switch (bpp) {
290	case 0:
291		return NULL;
292	default:
293		return (uint8_t*) &cache->vram[tileId << (2 + bpp)];
294	}
295}
296
297const uint16_t* mTileCacheGetPalette(struct mTileCache* cache, unsigned paletteId) {
298	unsigned bpp = cache->bpp;
299	switch (bpp) {
300	default:
301		return NULL;
302	case 1:
303		return &cache->palette[paletteId << 2];
304	case 2:
305		return &cache->palette[paletteId << 4];
306	case 3:
307		return &cache->palette[paletteId << 8];
308	}
309}