all repos — mgba @ 5e37df6cf5b7f9744bdbb64c550cf13a98892b35

mGBA Game Boy Advance Emulator

src/core/map-cache.c (view raw)

  1/* Copyright (c) 2013-2017 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/map-cache.h>
  7
  8#include <mgba-util/memory.h>
  9
 10void mMapCacheInit(struct mMapCache* cache) {
 11	// TODO: Reconfigurable cache for space savings
 12	cache->cache = NULL;
 13	cache->config = mMapCacheConfigurationFillShouldStore(0);
 14	cache->sysConfig = 0;
 15	cache->status = NULL;
 16}
 17
 18static void _freeCache(struct mMapCache* cache) {
 19	size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
 20	if (cache->cache) {
 21		mappedMemoryFree(cache->cache, 8 * 8 * sizeof(color_t) * tiles);
 22		cache->cache = NULL;
 23	}
 24	if (cache->status) {
 25		mappedMemoryFree(cache->status, tiles * sizeof(*cache->status));
 26		cache->status = NULL;
 27	}
 28}
 29
 30static void _redoCacheSize(struct mMapCache* cache) {
 31	if (!mMapCacheConfigurationIsShouldStore(cache->config)) {
 32		return;
 33	}
 34
 35	size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
 36	cache->cache = anonymousMemoryMap(8 * 8 * sizeof(color_t) * tiles);
 37	cache->status = anonymousMemoryMap(tiles * sizeof(*cache->status));
 38}
 39
 40void mMapCacheConfigure(struct mMapCache* cache, mMapCacheConfiguration config) {
 41	if (config == cache->config) {
 42		return;
 43	}
 44	_freeCache(cache);
 45	cache->config = config;
 46	_redoCacheSize(cache);
 47}
 48
 49void mMapCacheConfigureSystem(struct mMapCache* cache, mMapCacheSystemInfo config) {
 50	if (config == cache->sysConfig) {
 51		return;
 52	}
 53	_freeCache(cache);
 54	cache->sysConfig = config;
 55	_redoCacheSize(cache);
 56
 57	size_t mapSize = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
 58	cache->mapSize = mapSize << mMapCacheSystemInfoGetMapAlign(cache->sysConfig);
 59}
 60
 61void mMapCacheConfigureMap(struct mMapCache* cache, uint32_t mapStart) {
 62	size_t tiles = (1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig)) * (1 << mMapCacheSystemInfoGetTilesHigh(cache->sysConfig));
 63	memset(cache->status, 0, tiles * sizeof(*cache->status));
 64	cache->mapStart = mapStart;
 65}
 66
 67void mMapCacheDeinit(struct mMapCache* cache) {
 68	_freeCache(cache);
 69}
 70
 71void mMapCacheWriteVRAM(struct mMapCache* cache, uint32_t address) {
 72	if (address >= cache->mapStart && address < cache->mapStart + cache->mapSize) {
 73		address -= cache->mapStart;
 74		struct mMapCacheEntry* status = &cache->status[address >> mMapCacheSystemInfoGetMapAlign(cache->sysConfig)];
 75		++status->vramVersion;
 76		status->flags = mMapCacheEntryFlagsClearVramClean(status->flags);
 77		status->tileStatus[mMapCacheEntryFlagsGetPaletteId(status->flags)].vramClean = 0;
 78	}
 79}
 80
 81static inline void _cleanTile(struct mMapCache* cache, const color_t* tile, color_t* mapOut, const struct mMapCacheEntry* status) {
 82	size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
 83	int x, y;
 84	switch (mMapCacheEntryFlagsGetMirror(status->flags)) {
 85	case 0:
 86		memcpy(mapOut, tile, sizeof(color_t) * 8);
 87		memcpy(&mapOut[stride], &tile[0x08], sizeof(color_t) * 8);
 88		memcpy(&mapOut[stride * 2], &tile[0x10], sizeof(color_t) * 8);
 89		memcpy(&mapOut[stride * 3], &tile[0x18], sizeof(color_t) * 8);
 90		memcpy(&mapOut[stride * 4], &tile[0x20], sizeof(color_t) * 8);
 91		memcpy(&mapOut[stride * 5], &tile[0x28], sizeof(color_t) * 8);
 92		memcpy(&mapOut[stride * 6], &tile[0x30], sizeof(color_t) * 8);
 93		memcpy(&mapOut[stride * 7], &tile[0x38], sizeof(color_t) * 8);
 94		break;
 95	case 1:
 96		for (y = 0; y < 8; ++y) {
 97			for (x = 0; x < 8; ++x) {
 98				mapOut[y * stride + (7 - x)] = tile[y * 8 + x];
 99			}
100		}
101		break;
102	case 2:
103		memcpy(&mapOut[stride * 7], tile, sizeof(color_t) * 8);
104		memcpy(&mapOut[stride * 6], &tile[0x08], sizeof(color_t) * 8);
105		memcpy(&mapOut[stride * 5], &tile[0x10], sizeof(color_t) * 8);
106		memcpy(&mapOut[stride * 4], &tile[0x18], sizeof(color_t) * 8);
107		memcpy(&mapOut[stride * 3], &tile[0x20], sizeof(color_t) * 8);
108		memcpy(&mapOut[stride * 2], &tile[0x28], sizeof(color_t) * 8);
109		memcpy(&mapOut[stride], &tile[0x30], sizeof(color_t) * 8);
110		memcpy(mapOut, &tile[0x38], sizeof(color_t) * 8);
111		break;
112	case 3:
113		for (y = 0; y < 8; ++y) {
114			for (x = 0; x < 8; ++x) {
115				mapOut[(7 - y) * stride + (7 - x)] = tile[y * 8 + x];
116			}
117		}
118		break;
119	}
120}
121
122uint32_t mMapCacheTileId(struct mMapCache* cache, unsigned x, unsigned y) {
123	int tilesWide = mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
124	int tilesHigh = mMapCacheSystemInfoGetTilesHigh(cache->sysConfig);
125	int stride = 1 << mMapCacheSystemInfoGetMacroTileSize(cache->sysConfig);
126	x &= (1 << tilesWide) - 1;
127	y &= (1 << tilesHigh) - 1;
128	unsigned xMajor = x & ~(stride - 1);
129	unsigned yMajor = y >> mMapCacheSystemInfoGetMacroTileSize(cache->sysConfig);
130	x &= stride - 1;
131	y &= stride - 1;
132	yMajor <<= tilesWide;
133	y += xMajor + yMajor;
134	return stride * y + x;
135}
136
137void mMapCacheCleanTile(struct mMapCache* cache, struct mMapCacheEntry* entry, unsigned x, unsigned y) {
138	size_t location = mMapCacheTileId(cache, x, y);
139	struct mMapCacheEntry* status = &cache->status[location];
140	const color_t* tile = NULL;
141	if (!mMapCacheEntryFlagsIsVramClean(status->flags)) {
142		status->flags = mMapCacheEntryFlagsFillVramClean(status->flags);
143		cache->mapParser(cache, status, &cache->vram[cache->mapStart + (location << mMapCacheSystemInfoGetMapAlign(cache->sysConfig))]);
144	}
145	unsigned tileId = status->tileId + cache->tileStart;
146	if (tileId >= mTileCacheSystemInfoGetMaxTiles(cache->tileCache->sysConfig)) {
147		tileId = 0;
148	}
149	tile = mTileCacheGetTileIfDirty(cache->tileCache, status->tileStatus, tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
150	if (!tile) {
151		if (mMapCacheEntryFlagsIsVramClean(status->flags) && memcmp(status, &entry[location], sizeof(*entry)) == 0) {
152			return;
153		}
154		tile = mTileCacheGetTile(cache->tileCache, tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
155	}
156
157	size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
158	color_t* mapOut = &cache->cache[(y * stride + x) * 8];
159	_cleanTile(cache, tile, mapOut, status);
160	entry[location] = *status;
161}
162
163bool mMapCacheCheckTile(struct mMapCache* cache, const struct mMapCacheEntry* entry, unsigned x, unsigned y) {
164	size_t location = mMapCacheTileId(cache, x, y);
165	struct mMapCacheEntry* status = &cache->status[location];
166	int paletteId = mMapCacheEntryFlagsGetPaletteId(status->flags);
167	const color_t* tile = NULL;
168	if (mMapCacheEntryFlagsIsVramClean(status->flags) && memcmp(status, &entry[location], sizeof(*entry)) == 0) {
169		unsigned tileId = status->tileId + cache->tileStart;
170		if (tileId >= mTileCacheSystemInfoGetMaxTiles(cache->tileCache->sysConfig)) {
171			tileId = 0;
172		}
173		tile = mTileCacheGetTileIfDirty(cache->tileCache, &status->tileStatus[paletteId], tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
174		return !tile;
175	}
176	return false;
177}
178
179void mMapCacheCleanRow(struct mMapCache* cache, unsigned y) {
180	// TODO: Cache
181	int tilesWide = 1 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
182	int macroTile = (1 << mMapCacheSystemInfoGetMacroTileSize(cache->sysConfig)) - 1;
183	size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
184	int location = 0;
185	int x;
186	for (x = 0; x < tilesWide; ++x) {
187		if (!(x & macroTile)) {
188			location = mMapCacheTileId(cache, x, y);
189		} else {
190			++location;
191		}
192		struct mMapCacheEntry* status = &cache->status[location];
193		if (!mMapCacheEntryFlagsIsVramClean(status->flags)) {
194			status->flags = mMapCacheEntryFlagsFillVramClean(status->flags);
195			cache->mapParser(cache, status, &cache->vram[cache->mapStart + (location << mMapCacheSystemInfoGetMapAlign(cache->sysConfig))]);
196		}
197		unsigned tileId = status->tileId + cache->tileStart;
198		if (tileId >= mTileCacheSystemInfoGetMaxTiles(cache->tileCache->sysConfig)) {
199			tileId = 0;
200		}
201		const color_t* tile = mTileCacheGetTile(cache->tileCache, tileId, mMapCacheEntryFlagsGetPaletteId(status->flags));
202		color_t* mapOut = &cache->cache[(y * stride + x) * 8];
203		_cleanTile(cache, tile, mapOut, status);
204	}
205}
206
207const color_t* mMapCacheGetRow(struct mMapCache* cache, unsigned y) {
208	size_t stride = 8 << mMapCacheSystemInfoGetTilesWide(cache->sysConfig);
209	return &cache->cache[y * stride];
210}