Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions include/c2d/font.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ charWidthInfo_s* C2D_FontGetCharWidthInfo(C2D_Font font, int glyphIndex);
*/
void C2D_FontCalcGlyphPos(C2D_Font font, fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY);

/** @brief Calculate glyph position of a given Unicode codepoint. Uses a cached value when the codepoint is ASCII, flags are 0, and scales are 1.
* @param[in] font Font to read from, or NULL for system font
* @param[out] out Glyph position
* @param[in] codepoint The Unicode codepoint of the glyph
* @param[in] flags Misc flags
* @param[in] scaleX Size to scale in X
* @param[in] scaleY Size to scale in Y
*/
void C2D_FontCalcGlyphPosFromCodePoint(C2D_Font font, fontGlyphPos_s* out, u32 codepoint, u32 flags, float scaleX, float scaleY);

/** @brief Get the font info structure associated with the font
* @param[in] font Font to read from, or NULL for the system font
* @returns FINF associated with the font
Expand Down
2 changes: 1 addition & 1 deletion source/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,7 @@ void C2Di_FlushVtxBuf(void)
ctx->idxBufLastPos = ctx->idxBufPos;
}

void C2Di_Update(void)
void C2Di_Update()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect in C: () is the same as (...).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh woops, I didn't even mean to change that... This I do know.

{
C2Di_Context* ctx = C2Di_GetContext();
u32 flags = ctx->flags & C2DiF_DirtyAny;
Expand Down
46 changes: 46 additions & 0 deletions source/font.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#include "internal.h"
#include <c2d/font.h>

fontGlyphPos_s g_systemFontASCIICache[128];
u32 g_numFontSheetsCombined;

C2D_Font C2D_FontLoad(const char* filename)
{
FILE* f = fopen(filename, "rb");
Expand Down Expand Up @@ -51,6 +54,11 @@ static C2D_Font C2Di_PostLoadFont(C2D_Font font)
tex->border = 0;
tex->lodParam = 0;
}

for (i = 0; i < NUM_ASCII_CHARACTERS; i++)
{
fontCalcGlyphPos(&font->asciiCache[i], font->cfnt, fontGlyphIndexFromCodePoint(font->cfnt, i), 0, 1.0, 1.0);
}
}
return font;
}
Expand Down Expand Up @@ -238,9 +246,47 @@ charWidthInfo_s* C2D_FontGetCharWidthInfo(C2D_Font font, int glyphIndex)
void C2D_FontCalcGlyphPos(C2D_Font font, fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY)
{
if (!font)
{
fontCalcGlyphPos(out, fontGetSystemFont(), glyphIndex, flags, scaleX, scaleY);

if (out->sheetIndex < g_numFontSheetsCombined)
{
u32 indexWithinBigSheet = out->sheetIndex % SHEETS_PER_BIG_SHEET;
out->sheetIndex /= SHEETS_PER_BIG_SHEET;

// Readjust glyph UVs to account for being a part of the combined texture.
out->texcoord.top = (out->texcoord.top + (SHEETS_PER_BIG_SHEET - indexWithinBigSheet - 1)) / (float) SHEETS_PER_BIG_SHEET;
out->texcoord.bottom = (out->texcoord.bottom + (SHEETS_PER_BIG_SHEET - indexWithinBigSheet - 1)) / (float) SHEETS_PER_BIG_SHEET;
}
else
{
out->sheetIndex = out->sheetIndex - g_numFontSheetsCombined + g_numFontSheetsCombined / SHEETS_PER_BIG_SHEET;
}
}
else
{
fontCalcGlyphPos(out, font->cfnt, glyphIndex, flags, scaleX, scaleY);
}
}

void C2D_FontCalcGlyphPosFromCodePoint(C2D_Font font, fontGlyphPos_s* out, u32 codepoint, u32 flags, float scaleX, float scaleY)
{
// Building glyph positions is pretty expensive, but we could just store the results for plain ASCII.
if (codepoint < NUM_ASCII_CHARACTERS && flags == 0 && scaleX == 1 && scaleY == 1)
{
if (font)
{
*out = font->asciiCache[codepoint];
}
else
{
*out = g_systemFontASCIICache[codepoint];
}
}
else
{
C2D_FontCalcGlyphPos(font, out, C2D_FontGlyphIndexFromCodePoint(font, codepoint), 0, 1.0f, 1.0f);
}
}

FINF_s* C2D_FontGetInfo(C2D_Font font)
Expand Down
8 changes: 8 additions & 0 deletions source/internal.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once
#include <c2d/base.h>
#include <3ds/font.h>

typedef struct
{
Expand Down Expand Up @@ -77,6 +78,7 @@ struct C2D_Font_s
CFNT_s* cfnt;
C3D_Tex* glyphSheets;
float textScale;
fontGlyphPos_s asciiCache[128];
};

static inline C2Di_Context* C2Di_GetContext(void)
Expand Down Expand Up @@ -117,3 +119,9 @@ void C2Di_AppendQuad(void);
void C2Di_AppendVtx(float x, float y, float z, float u, float v, float ptx, float pty, u32 color);
void C2Di_FlushVtxBuf(void);
void C2Di_Update(void);

#define NUM_ASCII_CHARACTERS 128
extern fontGlyphPos_s g_systemFontASCIICache[NUM_ASCII_CHARACTERS];

#define SHEETS_PER_BIG_SHEET 32
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same comment from the hbmenu PR about not hardcoding this applies here. In citro2d, it's even more relevant, as citro2d supports using arbitrary fonts (not just the system font).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So note here that I intentionally limit the big sheets optimization to the system font only, since I'd like to assume that mkcbfnt will create an efficient amount of sheets anyway. What do you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not all fonts are necessarly going to be mkbcfnt generated. A trivial example is a different region's system font (which, yes, most of those do not have the same issue as the standard one, but is still a consideration), and less trivial would be a plugin or something that wants to use a game's assets, for whatever reason. It is relatively easy to access the assets of any installed title, so I could absolutely see that being done

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see... I think I can try applying the optimization to all fonts then. Though, are different regions' system fonts not just the system font? Also, do you all think it makes any sense to wrap the system font around a C2D_Font in order to coalesce the duplicate code?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are four different system fonts: "standard", Korean, Chinese Traditional, and Chinese Simplified. Also, the choice was originally made to have NULL be the system font on purpose; this allows the old API without font selection to continue to use the new API internally. I suppose having an internal dummy isn't the worst idea; that could be returned instead of NULL if one were to load the system font.

extern u32 g_numFontSheetsCombined;
58 changes: 43 additions & 15 deletions source/text.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,19 @@ static int C2Di_GlyphComp(const void* _g1, const void* _g2)
return ret;
}

static void fillSheet(C3D_Tex *tex, void *data, TGLP_s *glyphInfo)
{
tex->data = data;
tex->fmt = glyphInfo->sheetFmt;
tex->size = glyphInfo->sheetSize;
tex->width = glyphInfo->sheetWidth;
tex->height = glyphInfo->sheetHeight;
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)
| GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE);
tex->border = 0;
tex->lodParam = 0;
}

static void C2Di_TextEnsureLoad(void)
{
// Skip if already loaded
Expand All @@ -70,24 +83,39 @@ static void C2Di_TextEnsureLoad(void)
// Load the glyph texture sheets
CFNT_s* font = fontGetSystemFont();
TGLP_s* glyphInfo = fontGetGlyphInfo(font);
s_glyphSheets = malloc(sizeof(C3D_Tex)*glyphInfo->nSheets);
s_textScale = 30.0f / glyphInfo->cellHeight;

// The way TGLP_s is set up, all of a font's texture sheets are adjacent in memory and have the same size. We can
// reinterpet the memory to describe a smaller set of much taller textures if we'd like. If we choose the right size,
// we can get all of the ASCII glyphs under a single texture, which will massively improve performance by reducing
// texture swaps within a piece of all-English text down to 0! We don't need any extra linear allocating to do this!
u32 numSheetsBig = glyphInfo->nSheets / SHEETS_PER_BIG_SHEET;
u32 numSheetsSmall = glyphInfo->nSheets % SHEETS_PER_BIG_SHEET;
u32 numSheetsTotal = numSheetsBig + numSheetsSmall;
g_numFontSheetsCombined = glyphInfo->nSheets - numSheetsSmall;

s_glyphSheets = malloc(sizeof(C3D_Tex)*numSheetsTotal);
if (!s_glyphSheets)
svcBreak(USERBREAK_PANIC);

int i;
for (i = 0; i < glyphInfo->nSheets; i ++)
memset(s_glyphSheets, 0, sizeof(sizeof(C3D_Tex)*numSheetsTotal));
s_textScale = 30.0f / glyphInfo->cellHeight;
for (u32 i = 0; i < numSheetsBig; i++)
{
C3D_Tex* tex = &s_glyphSheets[i];
tex->data = fontGetGlyphSheetTex(font, i);
tex->fmt = glyphInfo->sheetFmt;
tex->size = glyphInfo->sheetSize;
tex->width = glyphInfo->sheetWidth;
tex->height = glyphInfo->sheetHeight;
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)
| GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_BORDER) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_BORDER);
tex->border = 0;
tex->lodParam = 0;
fillSheet(tex, fontGetGlyphSheetTex(font, i * SHEETS_PER_BIG_SHEET), glyphInfo);
tex->height = (uint16_t) (tex->height * SHEETS_PER_BIG_SHEET);
tex->size = tex->size * SHEETS_PER_BIG_SHEET;
}

for (u32 i = 0; i < numSheetsSmall; i++)
{
fillSheet(&s_glyphSheets[numSheetsBig + i], fontGetGlyphSheetTex(font, numSheetsBig * SHEETS_PER_BIG_SHEET + i), glyphInfo);
}

// Initialize system font ASCII cache for C2D_FontCalcGlyphPosFromCodePoint
for (int i = 0; i < NUM_ASCII_CHARACTERS; i++)
{
// This will readjust glyph UVs to account for being a part of the combined texture.
C2D_FontCalcGlyphPos(NULL, &g_systemFontASCIICache[i], fontGlyphIndexFromCodePoint(font, i), 0, 1.0, 1.0);
}
}

Expand Down Expand Up @@ -161,7 +189,7 @@ const char* C2D_TextFontParseLine(C2D_Text* text, C2D_Font font, C2D_TextBuf buf
p += units;

fontGlyphPos_s glyphData;
C2D_FontCalcGlyphPos(font, &glyphData, C2D_FontGlyphIndexFromCodePoint(font, code), 0, 1.0f, 1.0f);
C2D_FontCalcGlyphPosFromCodePoint(font, &glyphData, code, 0, 1.0f, 1.0f);
if (glyphData.width > 0.0f)
{
C2Di_Glyph* glyph = &buf->glyphs[buf->glyphCount++];
Expand Down