From cc048431f62e83bf840c68ff6fe02f87f3d8363d Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Mon, 4 Dec 2023 22:07:55 +1100 Subject: [PATCH] N64: Support loading UI textures as 16bpp instead of 32bpp to squeeze even more out of the 4 KB TMEM --- src/Drawer2D.c | 14 +++++++------ src/Entity.c | 2 +- src/Graphics.h | 7 ++++++- src/Graphics_N64.c | 47 +++++++++++++++++++++++++++++++++---------- src/TexturePack.c | 2 +- src/_GraphicsBase.h | 16 +++++++++------ src/freetype/ftobjs.c | 1 - 7 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/Drawer2D.c b/src/Drawer2D.c index 243aaf386..771bda728 100644 --- a/src/Drawer2D.c +++ b/src/Drawer2D.c @@ -301,13 +301,15 @@ void Drawer2D_MakeTextTexture(struct Texture* tex, struct DrawTextArgs* args) { } void Context2D_MakeTexture(struct Texture* tex, struct Context2D* ctx) { - Gfx_RecreateTexture(&tex->ID, &ctx->bmp, TEXTURE_FLAG_NONPOW2, false); + int flags = TEXTURE_FLAG_NONPOW2 | TEXTURE_FLAG_LOWRES; + Gfx_RecreateTexture(&tex->ID, &ctx->bmp, flags, false); + tex->Width = ctx->width; tex->Height = ctx->height; - - tex->uv.U1 = 0.0f; tex->uv.V1 = 0.0f; - tex->uv.U2 = (float)ctx->width / (float)ctx->bmp.width; - tex->uv.V2 = (float)ctx->height / (float)ctx->bmp.height; + + tex->uv.U1 = 0.0f; tex->uv.V1 = 0.0f; + tex->uv.U2 = (float)ctx->width / (float)ctx->bmp.width; + tex->uv.V2 = (float)ctx->height / (float)ctx->bmp.height; } cc_bool Drawer2D_ValidColorCodeAt(const cc_string* text, int i) { @@ -687,4 +689,4 @@ struct IGameComponent Drawer2D_Component = { OnInit, /* Init */ OnFree, /* Free */ OnReset, /* Reset */ -}; \ No newline at end of file +}; diff --git a/src/Entity.c b/src/Entity.c index c00e54d7c..380069c8c 100644 --- a/src/Entity.c +++ b/src/Entity.c @@ -326,7 +326,7 @@ static cc_result ApplySkin(struct Entity* e, struct Bitmap* bmp, struct Stream* if ((res = EnsurePow2Skin(e, bmp))) return res; e->SkinType = Utils_CalcSkinType(bmp); - if (!Gfx_CheckTextureSize(bmp->width, bmp->height)) { + if (!Gfx_CheckTextureSize(bmp->width, bmp->height, 0)) { Chat_Add1("&cSkin %s is too large", skin); } else { if (e->Model->flags & MODEL_FLAG_CLEAR_HAT) diff --git a/src/Graphics.h b/src/Graphics.h index 3a72859d1..ee328bf1e 100644 --- a/src/Graphics.h +++ b/src/Graphics.h @@ -58,6 +58,9 @@ CC_VAR extern struct _GfxData { struct Matrix View, Projection; /* Whether the graphics backend supports non power of two textures */ cc_bool SupportsNonPowTwoTextures; + /* Maximum total size in pixels a low resolution texture can consist of */ + /* NOTE: Not all graphics backends specify a value for this */ + int MaxLowResTexSize; } Gfx; extern GfxResourceID Gfx_defaultIb; @@ -73,13 +76,15 @@ extern const cc_string Gfx_LowPerfMessage; #define TEXTURE_FLAG_DYNAMIC 0x02 /* Texture is deliberately (and not accidentally) being created with non power of two dimensions */ #define TEXTURE_FLAG_NONPOW2 0x04 +/* Texture can fallback to 16 bpp when necessary (most backends don't do this) */ +#define TEXTURE_FLAG_LOWRES 0x08 #define LOWPERF_EXIT_MESSAGE "&eExited reduced performance mode" void Gfx_RecreateTexture(GfxResourceID* tex, struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps); void* Gfx_RecreateAndLockVb(GfxResourceID* vb, VertexFormat fmt, int count); -cc_bool Gfx_CheckTextureSize(int width, int height); +cc_bool Gfx_CheckTextureSize(int width, int height, cc_uint8 flags); /* Creates a new texture. (and also generates mipmaps if mipmaps) */ /* See TEXTURE_FLAG values for supported flags */ /* NOTE: Only set mipmaps to true if Gfx_Mipmaps is also true, because whether textures diff --git a/src/Graphics_N64.c b/src/Graphics_N64.c index 86f15a59f..27d7d08e1 100644 --- a/src/Graphics_N64.c +++ b/src/Graphics_N64.c @@ -28,10 +28,15 @@ void Gfx_Create(void) { //rdpq_debug_log(true); zbuffer = surface_alloc(FMT_RGBA16, display_get_width(), display_get_height()); - Gfx.MaxTexWidth = 128; - Gfx.MaxTexHeight = 128; - Gfx.MaxTexSize = 1024; // TMEM only has 4 KB in it + Gfx.MaxTexWidth = 256; + Gfx.MaxTexHeight = 256; Gfx.Created = true; + + // TMEM only has 4 KB in it, which can be interpreted as + // - 1024 32bpp pixels + // - 2048 16bpp pixels + Gfx.MaxTexSize = 1024; + Gfx.MaxLowResTexSize = 2048; Gfx.SupportsNonPowTwoTextures = true; Gfx_RestoreState(); @@ -116,7 +121,12 @@ typedef struct CCTexture { GLuint textureID; } CCTexture; +// A8 B8 G8 R8 > A1 B5 G5 B5 +#define To16BitPixel(src) \ + ((src & 0x80) >> 7) | ((src & 0xF800) >> 10) | ((src & 0xF80000) >> 13) | ((src & 0xF8000000) >> 16); + static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { + cc_bool bit16 = flags & TEXTURE_FLAG_LOWRES; CCTexture* tex = Mem_Alloc(1, sizeof(CCTexture), "texture"); glGenTextures(1, &tex->textureID); @@ -125,19 +135,34 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_boo glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - tex->surface = surface_alloc(FMT_RGBA32, bmp->width, bmp->height); - + tex->surface = surface_alloc(bit16 ? FMT_RGBA16 : FMT_RGBA32, bmp->width, bmp->height); surface_t* fb = &tex->surface; cc_uint32* src = (cc_uint32*)bmp->scan0; cc_uint8* dst = (cc_uint8*)fb->buffer; - - for (int y = 0; y < bmp->height; y++) - { - Mem_Copy(dst + y * fb->stride, - src + y * bmp->width, - bmp->width * 4); + + if (bit16) { + // 16 bpp requires reducing A8R8G8B8 to A1R5G5B5 + for (int y = 0; y < bmp->height; y++) + { + cc_uint32* src_row = src + y * bmp->width; + cc_uint16* dst_row = (cc_uint16*)(dst + y * fb->stride); + + for (int x = 0; x < bmp->width; x++) + { + dst_row[x] = To16BitPixel(src_row[x]); + } + } + } else { + // 32 bpp can just be copied straight across + for (int y = 0; y < bmp->height; y++) + { + Mem_Copy(dst + y * fb->stride, + src + y * bmp->width, + bmp->width * 4); + } } + rdpq_texparms_t params = { .s.repeats = (flags & TEXTURE_FLAG_NONPOW2) ? 1 : REPEAT_INFINITE, diff --git a/src/TexturePack.c b/src/TexturePack.c index 7f9efab1c..2ed20adea 100644 --- a/src/TexturePack.c +++ b/src/TexturePack.c @@ -153,7 +153,7 @@ cc_bool Atlas_TryChange(struct Bitmap* atlas) { return false; } - if (!Gfx_CheckTextureSize(tileSize, tileSize)) { + if (!Gfx_CheckTextureSize(tileSize, tileSize, 0)) { Chat_AddRaw("&cUnable to use terrain.png from the texture pack."); Chat_Add4("&c Tile size is (%i,%i), your GPU supports (%i,%i) at most.", &tileSize, &tileSize, &Gfx.MaxTexWidth, &Gfx.MaxTexHeight); diff --git a/src/_GraphicsBase.h b/src/_GraphicsBase.h index c461470f2..16f7d67a0 100644 --- a/src/_GraphicsBase.h +++ b/src/_GraphicsBase.h @@ -350,13 +350,17 @@ static CC_NOINLINE int CalcMipmapsLevels(int width, int height) { } } -cc_bool Gfx_CheckTextureSize(int width, int height) { +cc_bool Gfx_CheckTextureSize(int width, int height, cc_uint8 flags) { + int maxSize; if (width > Gfx.MaxTexWidth) return false; if (height > Gfx.MaxTexHeight) return false; + + maxSize = Gfx.MaxTexSize; + // low resolution textures may support higher sizes (e.g. Nintendo 64) + if ((flags & TEXTURE_FLAG_LOWRES) && Gfx.MaxLowResTexSize) + maxSize = Gfx.MaxLowResTexSize; - if (Gfx.MaxTexSize && (width * height > Gfx.MaxTexSize)) - return false; - return true; + return maxSize == 0 || (width * height <= maxSize); } static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps); @@ -370,7 +374,7 @@ GfxResourceID Gfx_CreateTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipm } if (Gfx.LostContext) return 0; - if (!Gfx_CheckTextureSize(bmp->width, bmp->height)) return 0; + if (!Gfx_CheckTextureSize(bmp->width, bmp->height, flags)) return 0; return Gfx_AllocTexture(bmp, flags, mipmaps); } @@ -448,4 +452,4 @@ struct IGameComponent Gfx_Component = { /* first component freed, even though it MUST be the last */ /* Instead, Game.c calls Gfx_Free after first freeing all */ /* the other game components. */ -}; \ No newline at end of file +}; diff --git a/src/freetype/ftobjs.c b/src/freetype/ftobjs.c index 63de58cc3..8f8aa85fe 100644 --- a/src/freetype/ftobjs.c +++ b/src/freetype/ftobjs.c @@ -2961,7 +2961,6 @@ FT_Error error; FT_Memory memory; FT_Module module = NULL; - FT_UInt nn; memory = library->memory; error = FT_Err_Ok;