all repos — mgba @ 1ac4a716cc6d0d5ec7ec3733116d28d45049b5fd

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