mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-05-23 14:41:33 -04:00
Merge pull request #360 from TwilitRealm/26-04-13-font-opts
Text Rendering optimizations
This commit is contained in:
@@ -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[];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user