Skip to content

Commit

Permalink
imageview: Optimize RAM usage in DS mode by adding LRU cache for font…
Browse files Browse the repository at this point in the history
… tiles
  • Loading branch information
RocketRobz committed Apr 27, 2024
1 parent 931a582 commit 8dfcdfa
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 61 deletions.
121 changes: 69 additions & 52 deletions imageview/arm9/source/graphics/FontGraphic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

#include "common/tonccpy.h"

u8 *FontGraphic::lastUsedLoc = (u8*)0x08000000;

u8 FontGraphic::textBuf[256 * 192];

std::map<char16_t, std::array<char16_t, 3>> FontGraphic::arabicPresentationForms = {
Expand Down Expand Up @@ -90,19 +88,15 @@ char16_t FontGraphic::arabicForm(char16_t current, char16_t prev, char16_t next)
return current;
}

FontGraphic::FontGraphic(const std::vector<std::string> &paths, bool useExpansionPak) : useExpansionPak(useExpansionPak) {
FILE *file = nullptr;
FontGraphic::FontGraphic(const std::vector<std::string> &paths, const bool set_useTileCache) {
for (const auto &path : paths) {
file = fopen(path.c_str(), "rb");
if (file)
break;
}

useTileCache = set_useTileCache;
if (file) {
if (useExpansionPak && *(u16*)(0x020000C0) == 0 && lastUsedLoc == (u8*)0x08000000) {
lastUsedLoc += 0x01000000;
}

// Get file size
fseek(file, 0, SEEK_END);
u32 fileSize = ftell(file);
Expand All @@ -121,14 +115,8 @@ FontGraphic::FontGraphic(const std::vector<std::string> &paths, bool useExpansio
// Load character glyphs
tileAmount = (chunkSize - 0x10) / tileSize;
fseek(file, 4, SEEK_CUR);
if (useExpansionPak) {
fontTiles = lastUsedLoc;
lastUsedLoc += tileSize * tileAmount;

u8 *buf = new u8[tileSize * tileAmount];
fread(buf, tileSize, tileAmount, file);
tonccpy(fontTiles, buf, tileSize * tileAmount);
delete[] buf;
if (useTileCache) {
fontTiles = new u8[tileSize * (tileAmount>tileCacheCount ? tileCacheCount : tileAmount)];
} else {
fontTiles = new u8[tileSize * tileAmount];
fread(fontTiles, tileSize, tileAmount, file);
Expand All @@ -141,32 +129,17 @@ FontGraphic::FontGraphic(const std::vector<std::string> &paths, bool useExpansio
fseek(file, locHDWC-4, SEEK_SET);
fread(&chunkSize, 4, 1, file);
fseek(file, 8, SEEK_CUR);
if (useExpansionPak) {
fontWidths = lastUsedLoc;
lastUsedLoc += 3 * tileAmount;

u8 *buf = new u8[3 * tileAmount];
fread(buf, 3, tileAmount, file);
tonccpy(fontWidths, buf, 3 * tileAmount);
delete[] buf;
} else {
fontWidths = new u8[3 * tileAmount];
fread(fontWidths, 3, tileAmount, file);
}
fontWidths = new u8[3 * tileAmount];
fread(fontWidths, 3, tileAmount, file);

// Load character maps
if (useExpansionPak) {
fontMap = (u16*)lastUsedLoc;
lastUsedLoc += tileAmount * sizeof(u16);
} else {
fontMap = new u16[tileAmount];
}
fontMap = new u16[tileAmount];

fseek(file, 0x28, SEEK_SET);
u32 locPAMC, mapType;
fread(&locPAMC, 4, 1, file);

while (locPAMC < fileSize) {
while (locPAMC && locPAMC < fileSize) {
u16 firstChar, lastChar;
fseek(file, locPAMC, SEEK_SET);
fread(&firstChar, 2, 1, file);
Expand Down Expand Up @@ -202,22 +175,20 @@ FontGraphic::FontGraphic(const std::vector<std::string> &paths, bool useExpansio
}
}
}
fclose(file);
questionMark = getCharIndex(0xFFFD);
if (questionMark == 0)
questionMark = getCharIndex('?');
}
}

FontGraphic::~FontGraphic(void) {
if (!useExpansionPak) {
if (fontTiles)
delete[] fontTiles;
if (fontWidths)
delete[] fontWidths;
if (fontMap)
delete[] fontMap;
}
fclose(file);
if (fontTiles)
delete[] fontTiles;
if (fontWidths)
delete[] fontWidths;
if (fontMap)
delete[] fontMap;
}

u16 FontGraphic::getCharIndex(char16_t c) {
Expand Down Expand Up @@ -430,14 +401,60 @@ ITCM_CODE void FontGraphic::print(int x, int y, bool top, std::u16string_view te
index = getCharIndex(*it);
}

// Don't draw off screen chars
if (x >= 0 && x + fontWidths[(index * 3) + 2] < 256 && y >= 0 && y + tileHeight < 192) {
u8 *dst = textBuf + x + fontWidths[(index * 3)];
for (int i = 0; i < tileHeight; i++) {
for (int j = 0; j < tileWidth; j++) {
u8 px = fontTiles[(index * tileSize) + (i * tileWidth + j) / 4] >> ((3 - ((i * tileWidth + j) % 4)) * 2) & 3;
if (px)
dst[(y + i) * 256 + j] = px + 0xF8;
if (useTileCache) {
bool found = false;
bool overwrite = true;
u8 cachePos = 0;
for (u8 i = 0; i < tileCacheCount; i++) {
if (!cacheAllocated[i]) {
indexCache[i] = index;
cachePos = i;
cacheAllocated[i] = true;
overwrite = false;
break;
} else if (indexCache[i] == index) {
cachePos = i;
found = true;
overwrite = false;
break;
}
}

if (overwrite) {
nextCachePos++;
if (nextCachePos == tileCacheCount) {
nextCachePos = 0;
}
cachePos = nextCachePos;
indexCache[cachePos] = index;
}

if (!found && file) {
fseek(file, 0x40+(index * tileSize), SEEK_SET);
fread(fontTiles+(cachePos * tileSize), tileSize, 1, file);
}

// Don't draw off screen chars
if (x >= 0 && x + fontWidths[(index * 3) + 2] < 256 && y >= 0 && y + tileHeight < 192) {
u8 *dst = textBuf + x + fontWidths[(index * 3)];
for (int i = 0; i < tileHeight; i++) {
for (int j = 0; j < tileWidth; j++) {
u8 px = fontTiles[(cachePos * tileSize) + (i * tileWidth + j) / 4] >> ((3 - ((i * tileWidth + j) % 4)) * 2) & 3;
if (px)
dst[(y + i) * 256 + j] = px + 0xF8;
}
}
}
} else {
// Don't draw off screen chars
if (x >= 0 && x + fontWidths[(index * 3) + 2] < 256 && y >= 0 && y + tileHeight < 192) {
u8 *dst = textBuf + x + fontWidths[(index * 3)];
for (int i = 0; i < tileHeight; i++) {
for (int j = 0; j < tileWidth; j++) {
u8 px = fontTiles[(index * tileSize) + (i * tileWidth + j) / 4] >> ((3 - ((i * tileWidth + j) % 4)) * 2) & 3;
if (px)
dst[(y + i) * 256 + j] = px + 0xF8;
}
}
}
}
Expand Down
12 changes: 8 additions & 4 deletions imageview/arm9/source/graphics/FontGraphic.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#include <string_view>
#include <vector>

#define tileCacheCount 128

enum class Alignment {
left,
center,
Expand All @@ -24,13 +26,15 @@ class FontGraphic {

static char16_t arabicForm(char16_t current, char16_t prev, char16_t next);

static u8 *lastUsedLoc;

bool useExpansionPak = false;
FILE* file = nullptr;
bool useTileCache = false;
u8 tileWidth = 0, tileHeight = 0;
u16 tileSize = 0;
int tileAmount = 0;
u16 questionMark = 0;
u16 indexCache[tileCacheCount] = {0xFFFF};
bool cacheAllocated[tileCacheCount] = {false};
u8 nextCachePos = 0xFF;
u8 *fontTiles = nullptr;
u8 *fontWidths = nullptr;
u16 *fontMap = nullptr;
Expand All @@ -42,7 +46,7 @@ class FontGraphic {

static std::u16string utf8to16(std::string_view text);

FontGraphic(const std::vector<std::string> &paths, const bool useExpansionPak);
FontGraphic(const std::vector<std::string> &paths, const bool set_useTileCache);

~FontGraphic(void);

Expand Down
10 changes: 5 additions & 5 deletions imageview/arm9/source/graphics/fontHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ bool fileExists(std::vector<std::string_view> paths) {
}

void fontInit() {
bool useExpansionPak = (sys().isRegularDS() && ((*(u16*)(0x020000C0) != 0 && *(u16*)(0x020000C0) != 0x5A45) || *(vu16*)(0x08240000) == 1) && (io_dldi_data->ioInterface.features & FEATURE_SLOT_NDS));
// const bool useExpansionPak = (sys().isRegularDS() && ((*(u16*)(0x020000C0) != 0 && *(u16*)(0x020000C0) != 0x5A45) || *(vu16*)(0x08240000) == 1) && (io_dldi_data->ioInterface.features & FEATURE_SLOT_NDS));
const bool useTileCache = (!dsiFeatures() && !sys().dsDebugRam());

// Unload fonts if already loaded
if (smallFont)
Expand All @@ -41,13 +42,12 @@ void fontInit() {
// Load font graphics
std::string fontPath = std::string(sys().isRunFromSD() ? "sd:" : "fat:") + "/_nds/TWiLightMenu/extras/fonts/" + ms().font;
std::string defaultPath = std::string(sys().isRunFromSD() ? "sd:" : "fat:") + "/_nds/TWiLightMenu/extras/fonts/Default";
bool dsiFont = dsiFeatures() || sys().dsDebugRam() || useExpansionPak;
smallFont = new FontGraphic({fontPath + (dsiFont ? "/small-dsi.nftr" : "/small-ds.nftr"), fontPath + "/small.nftr", defaultPath + (dsiFont ? "/small-dsi.nftr" : "/small-ds.nftr"), "nitro:/graphics/font/small.nftr"}, useExpansionPak);
smallFont = new FontGraphic({fontPath + "/small-dsi.nftr", fontPath + "/small.nftr", defaultPath + "/small-dsi.nftr", "nitro:/graphics/font/small.nftr"}, useTileCache);
// If custom small font but no custom large font, use small font as large font
if (fileExists({fontPath + (dsiFont ? "/small-dsi.nftr" : "/small-ds.nftr"), fontPath + "/small.nftr"}) && !fileExists({fontPath + (dsiFont ? "/large-dsi.nftr" : "/large-ds.nftr"), fontPath + "/large.nftr"}))
if (fileExists({fontPath + "/small-dsi.nftr", fontPath + "/small.nftr"}) && !fileExists({fontPath + "/large-dsi.nftr", fontPath + "/large.nftr"}))
largeFont = smallFont;
else
largeFont = new FontGraphic({fontPath + (dsiFont ? "/large-dsi.nftr" : "/large-ds.nftr"), fontPath + "/large.nftr", defaultPath + (dsiFont ? "/large-dsi.nftr" : "/large-ds.nftr"), "nitro:/graphics/font/large.nftr"}, useExpansionPak);
largeFont = new FontGraphic({fontPath + "/large-dsi.nftr", fontPath + "/large.nftr", defaultPath + "/large-dsi.nftr", "nitro:/graphics/font/large.nftr"}, useTileCache);

// Load palettes
u16 palette[] = {
Expand Down

0 comments on commit 8dfcdfa

Please sign in to comment.