Merge pull request #360 from TwilitRealm/26-04-13-font-opts

Text Rendering optimizations
This commit is contained in:
TakaRikka
2026-04-14 02:38:59 -07:00
committed by GitHub
5 changed files with 120 additions and 47 deletions
+2
View File
@@ -220,10 +220,12 @@ using std::isnan;
// Some basic macros that are more convenient than putting down #if blocks for one-line changes.
#if TARGET_PC
#define IF_DUSK(statement) statement
#define IF_DUSK_ARG(expr) , expr
#define IF_NOT_DUSK(statement)
#define DUSK_IF_ELSE(dusk, orig) dusk
#else
#define IF_DUSK(statement)
#define IF_DUSK_ARG(expr)
#define IF_NOT_DUSK(statement) statement
#define DUSK_IF_ELSE(dusk, orig) orig
#endif
@@ -5,6 +5,17 @@
#include <cstring>
#include "dusk/endian.h"
#if TARGET_PC
struct FontDrawContext {
bool isTextureLoaded = false;
};
#define FONT_DRAW_CTX , FontDrawContext* context
#define FONT_DRAW_CTX_ARG , context
#else
#define FONT_DRAW_CTX
#define FONT_DRAW_CTX_ARG
#endif
/**
* @ingroup jsystem-jutility
*
@@ -84,7 +95,12 @@ public:
/* 0x0C */ virtual void setGX() = 0;
/* 0x10 */ virtual void setGX(JUtility::TColor col1, JUtility::TColor col2) { setGX(); }
/* 0x14 */ virtual f32 drawChar_scale(f32 a1, f32 a2, f32 a3, f32 a4, int a5, bool a6) = 0;
/* 0x14 */ virtual f32 drawChar_scale(f32 a1, f32 a2, f32 a3, f32 a4, int a5, bool a6 FONT_DRAW_CTX) = 0;
#if TARGET_PC
f32 drawChar_scale(f32 a1, f32 a2, f32 a3, f32 a4, int a5, bool a6) {
return drawChar_scale(a1, a2, a3, a4, a5, a6, nullptr);
}
#endif
/* 0x18 */ virtual int getLeading() const = 0;
/* 0x1C */ virtual s32 getAscent() const = 0;
/* 0x20 */ virtual s32 getDescent() const = 0;
@@ -97,6 +113,11 @@ public:
/* 0x3C */ virtual ResFONT* getResFont() const = 0;
/* 0x40 */ virtual bool isLeadByte(int a1) const = 0;
#if TARGET_PC
virtual void pushDrawState() = 0;
virtual void popDrawState() = 0;
#endif
static bool isLeadByte_1Byte(int b) {
return false;
}
@@ -18,10 +18,6 @@ struct BlockHeader {
BE(u32) size;
};
#if TARGET_PC
struct GlyphTextures;
#endif
/**
* @ingroup jsystem-jutility
*
@@ -31,7 +27,7 @@ public:
virtual ~JUTResFont();
virtual void setGX();
virtual void setGX(JUtility::TColor, JUtility::TColor);
virtual f32 drawChar_scale(f32, f32, f32, f32, int, bool);
virtual f32 drawChar_scale(f32, f32, f32, f32, int, bool FONT_DRAW_CTX);
virtual int getLeading() const;
virtual s32 getAscent() const;
virtual s32 getDescent() const;
@@ -43,7 +39,7 @@ public:
virtual int getFontType() const;
virtual ResFONT* getResFont() const;
virtual bool isLeadByte(int) const;
virtual void loadImage(int, GXTexMapID);
virtual void loadImage(int, GXTexMapID FONT_DRAW_CTX);
virtual void setBlock();
JUTResFont(ResFONT const*, JKRHeap*);
@@ -53,10 +49,15 @@ public:
bool initiate(ResFONT const*, JKRHeap*);
bool protected_initiate(ResFONT const*, JKRHeap*);
void countBlock();
void loadFont(int, GXTexMapID, JUTFont::TWidth*);
void loadFont(int, GXTexMapID, JUTFont::TWidth* FONT_DRAW_CTX);
int getFontCode(int) const;
int convertSjis(int, BE(u16)*) const;
#if TARGET_PC
void pushDrawState() override;
void popDrawState() override;
#endif
inline void delete_and_initialize() {
deleteMemBlocks_ResFont();
initialize_state();
@@ -86,6 +87,16 @@ public:
/* 0x66 */ u16 field_0x66;
/* 0x68 */ u16 mMaxCode;
/* 0x6C */ const IsLeadByte_func* mIsLeadByte;
#if TARGET_PC
// Dusk change: we use a single large texture for all characters.
// This enables better draw call merging, ideally enabling entire blocks of
// text to be one draw call.
TGXTexObj mJoinedTextureObject;
u16 mJoinedTextureHeight;
void initJoinedTexture();
#endif
};
extern u8 const JUTResFONT_Ascfont_fix12[];
+9 -2
View File
@@ -211,6 +211,11 @@ f32 J2DPrint::parse(const u8* pString, int length, int param_2, u16* param_3,
local_bc.a = local_bc.a * alpha / 0xFF;
mFont->setGradColor(local_b8, field_0x22 ? local_bc : local_b8);
#if TARGET_PC
FontDrawContext context;
mFont->pushDrawState();
#endif
do {
bool b2ByteCharacter = false;
bool r25;
@@ -312,9 +317,9 @@ f32 J2DPrint::parse(const u8* pString, int length, int param_2, u16* param_3,
} else {
if (param_6) {
if (param_3 != NULL) {
mFont->drawChar_scale(mCursorH + (s16)param_3[someIndex], mCursorV, (s32)mScaleX, (s32)mScaleY, iCharacter, true);
mFont->drawChar_scale(mCursorH + (s16)param_3[someIndex], mCursorV, (s32)mScaleX, (s32)mScaleY, iCharacter, true IF_DUSK_ARG(&context));
} else {
mFont->drawChar_scale(mCursorH, mCursorV, (s32)mScaleX, (s32)mScaleY, iCharacter, true);
mFont->drawChar_scale(mCursorH, mCursorV, (s32)mScaleX, (s32)mScaleY, iCharacter, true IF_DUSK_ARG(&context));
}
}
mCursorH += field_0x34;
@@ -353,6 +358,8 @@ f32 J2DPrint::parse(const u8* pString, int length, int param_2, u16* param_3,
iCharacter = *(pString++);
} while (true);
IF_DUSK(mFont->popDrawState());
if (param_3 != NULL) {
param_3[someIndex] = 0xFFFF;
}
+69 -37
View File
@@ -6,26 +6,13 @@
#include "JSystem/JUtility/JUTAssert.h"
#include "JSystem/JUtility/JUTConsole.h"
#include <gx.h>
#include "absl/container/node_hash_map.h"
#if TARGET_PC
struct GlyphTextures {
absl::node_hash_map<int, TGXTexObj> textures;
};
#endif
JUTResFont::JUTResFont() {
#if TARGET_PC
mGlyphTextures = new GlyphTextures();
#endif
initialize_state();
JUTFont::initialize_state();
}
JUTResFont::JUTResFont(const ResFONT* pFont, JKRHeap* pHeap) {
#if TARGET_PC
mGlyphTextures = new GlyphTextures();
#endif
initialize_state();
JUTFont::initialize_state();
initiate(pFont, pHeap);
@@ -33,10 +20,7 @@ JUTResFont::JUTResFont(const ResFONT* pFont, JKRHeap* pHeap) {
JUTResFont::~JUTResFont() {
#if TARGET_PC
for (auto& pair : mGlyphTextures->textures) {
pair.second.reset();
}
delete mGlyphTextures;
mJoinedTextureObject.reset();
#endif
if (mValid) {
@@ -70,6 +54,33 @@ bool JUTResFont::initiate(const ResFONT* pFont, JKRHeap* pHeap) {
return true;
}
#if TARGET_PC
void JUTResFont::initJoinedTexture() {
if (mGly1BlockNum != 1) {
CRASH("mGly1BlockNum must be 1!");
}
const auto& block = *mpGlyphBlocks[0];
if (block.textureWidth % 8 != 0 || block.textureHeight % 8 != 0) {
// Idk how the GameCube's tiling texture format works so this is a safety check.
CRASH("Texture size not divisible!");
}
int pageCount = 0;
u32 pageNumCells = block.numRows * block.numColumns;
for (u32 code = block.startCode; code < block.endCode; code += pageNumCells) {
pageCount += 1;
}
mJoinedTextureHeight = block.textureHeight * pageCount;
GXInitTexObj(&mJoinedTextureObject, block.data, block.textureWidth,
mJoinedTextureHeight, static_cast<GXTexFmt>(block.textureFormat.host()),
GX_CLAMP, GX_CLAMP, false);
GXInitTexObjLOD(&mJoinedTextureObject, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, 0U, 0U, GX_ANISO_1);
}
#endif
bool JUTResFont::protected_initiate(const ResFONT* pFont, JKRHeap* pHeap) {
void** p;
delete_and_initialize();
@@ -100,8 +111,10 @@ bool JUTResFont::protected_initiate(const ResFONT* pFont, JKRHeap* pHeap) {
mpMapBlocks = JKR_NEW_ARRAY_ARGS(ResFONT::MAP1*, mMap1BlockNum, p);
}
setBlock();
return true;
IF_DUSK(initJoinedTexture());
return true;
}
void JUTResFont::countBlock() {
@@ -231,14 +244,14 @@ void JUTResFont::setGX(JUtility::TColor col1, JUtility::TColor col2) {
}
f32 JUTResFont::drawChar_scale(f32 pos_x, f32 pos_y, f32 scale_x, f32 scale_y, int str_int,
bool flag) {
bool flag FONT_DRAW_CTX) {
f32 x1;
f32 x2;
f32 y1;
JUT_ASSERT(378, mValid);
JUTFont::TWidth width;
loadFont(str_int, GX_TEXMAP0, &width);
loadFont(str_int, GX_TEXMAP0, &width FONT_DRAW_CTX_ARG);
if ((mFixed) || (!flag)) {
x1 = pos_x;
@@ -258,15 +271,26 @@ f32 JUTResFont::drawChar_scale(f32 pos_x, f32 pos_y, f32 scale_x, f32 scale_y, i
f32 y2 = getDescent() * (scale_y / getHeight()) + pos_y;
u16 texW = mpGlyphBlocks[field_0x66]->textureWidth;
#if TARGET_PC
u16 texH = mJoinedTextureHeight;
#else
u16 texH = mpGlyphBlocks[field_0x66]->textureHeight;
#endif
u16 cellW = mpGlyphBlocks[field_0x66]->cellWidth;
u16 cellH = mpGlyphBlocks[field_0x66]->cellHeight;
s32 u1 = (mWidth * 0x8000) / texW;
s32 v1 = (mHeight * 0x8000) / texH;
s32 u2 = ((mWidth + cellW) * 0x8000) / texW;
s32 v2 = ((mHeight + cellH) * 0x8000) / texH;
#if TARGET_PC
if (!context) {
pushDrawState();
}
#else
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
#endif
GXBegin(GX_QUADS, GX_VTXFMT0, 4);
// Bottom Left
@@ -290,18 +314,33 @@ f32 JUTResFont::drawChar_scale(f32 pos_x, f32 pos_y, f32 scale_x, f32 scale_y, i
GXTexCoord2u16(u1, v2);
GXEnd();
#if TARGET_PC
if (!context) {
popDrawState();
}
#else
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 0);
#endif
return retval;
}
void JUTResFont::loadFont(int code, GXTexMapID texMapID, JUTFont::TWidth* pDstWidth) {
#if TARGET_PC
void JUTResFont::pushDrawState() {
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
}
void JUTResFont::popDrawState() {
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 0);
}
#endif
void JUTResFont::loadFont(int code, GXTexMapID texMapID, JUTFont::TWidth* pDstWidth FONT_DRAW_CTX) {
if (pDstWidth != 0) {
getWidthEntry(code, pDstWidth);
}
int fontCode = getFontCode(code);
loadImage(fontCode, texMapID);
loadImage(fontCode, texMapID FONT_DRAW_CTX_ARG);
}
void JUTResFont::getWidthEntry(int code, JUTFont::TWidth* i_width) const {
@@ -403,7 +442,7 @@ int JUTResFont::getFontCode(int chr) const {
return ret;
}
void JUTResFont::loadImage(int code, GXTexMapID id){
void JUTResFont::loadImage(int code, GXTexMapID id FONT_DRAW_CTX){
int i = 0;
for (; i < mGly1BlockNum; i++)
{
@@ -435,22 +474,15 @@ void JUTResFont::loadImage(int code, GXTexMapID id){
mHeight = cellRow * cellH;
#if TARGET_PC
const auto found = mGlyphTextures->textures.find(pageIdx);
GXTexObj* texObj;
if (found == mGlyphTextures->textures.end()) {
texObj = &mGlyphTextures->textures[pageIdx];
void* pImg = &mpGlyphBlocks[i]->data[pageIdx * mpGlyphBlocks[i]->textureSize];
GXInitTexObj(texObj, pImg, mpGlyphBlocks[i]->textureWidth,
mpGlyphBlocks[i]->textureHeight, (GXTexFmt)(u16)mpGlyphBlocks[i]->textureFormat,
GX_CLAMP, GX_CLAMP, 0);
mHeight += texH * pageIdx;
GXInitTexObjLOD(texObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, 0U, 0U, GX_ANISO_1);
} else {
texObj = &found->second;
if (!context || !context->isTextureLoaded) {
GXLoadTexObj(&mJoinedTextureObject, id);
if (context) {
context->isTextureLoaded = true;
}
}
GXLoadTexObj(texObj, id);
// Gets used by some other code.
mTexPageIdx = pageIdx;
field_0x66 = i;