Make JUTResFont load all texture data into one texture

Together with the previous change, this enables entire blocks of text to be rendered in one draw call.
This commit is contained in:
PJB3005
2026-04-13 19:09:16 +02:00
parent 0a95a9e538
commit 1dac80f3a9
3 changed files with 51 additions and 42 deletions
@@ -7,8 +7,7 @@
#if TARGET_PC
struct FontDrawContext {
int loadedBlock = -1;
int loadedPage = -1;
bool isTextureLoaded = false;
};
#define FONT_DRAW_CTX , FontDrawContext* context
#define FONT_DRAW_CTX_ARG , context
@@ -18,10 +18,6 @@ struct BlockHeader {
BE(u32) size;
};
#if TARGET_PC
struct GlyphTextures;
#endif
/**
* @ingroup jsystem-jutility
*
@@ -91,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[];
+40 -36
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() {
@@ -258,8 +271,13 @@ 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;
@@ -456,26 +474,12 @@ void JUTResFont::loadImage(int code, GXTexMapID id FONT_DRAW_CTX){
mHeight = cellRow * cellH;
#if TARGET_PC
if (!context || context->loadedPage != pageIdx || context->loadedBlock != i) {
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);
GXInitTexObjLOD(texObj, GX_LINEAR, GX_LINEAR, 0.0f, 0.0f, 0.0f, 0U, 0U, GX_ANISO_1);
} else {
texObj = &found->second;
}
GXLoadTexObj(texObj, id);
mHeight += texH * pageIdx;
if (!context || !context->isTextureLoaded) {
GXLoadTexObj(&mJoinedTextureObject, id);
if (context) {
context->loadedBlock = i;
context->loadedPage = pageIdx;
context->isTextureLoaded = true;
}
}