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}