Calculate total mipmap texture size with geometric series formula

This commit is contained in:
fincs 2017-02-11 18:29:41 +01:00
parent 0fd245b171
commit 6ec42ae8a1
2 changed files with 36 additions and 33 deletions

View File

@ -34,7 +34,6 @@ typedef struct ALIGN(8)
} C3D_TexInitParams;
bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexInitParams p);
void* C3D_TexGetLevel(C3D_Tex* tex, int level, u32* size);
void C3D_TexUploadLevel(C3D_Tex* tex, const void* data, int level);
void C3D_TexGenerateMipmap(C3D_Tex* tex);
void C3D_TexBind(int unitId, C3D_Tex* tex);
@ -46,6 +45,32 @@ static inline int C3D_TexCalcMaxLevel(u32 width, u32 height)
return (31-__builtin_clz(width < height ? width : height)) - 3; // avoid sizes smaller than 8
}
static inline u32 C3D_TexCalcLevelSize(u32 size, int level)
{
return size >> (2*level);
}
static inline u32 C3D_TexCalcTotalSize(u32 size, int maxLevel)
{
/*
S = s + sr + sr^2 + sr^3 + ... + sr^n
Sr = sr + sr^2 + sr^3 + ... + sr^(n+1)
S-Sr = s - sr^(n+1)
S(1-r) = s(1 - r^(n+1))
S = s (1 - r^(n+1)) / (1-r)
r = 1/4
1-r = 3/4
S = 4s (1 - (1/4)^(n+1)) / 3
S = 4s (1 - 1/4^(n+1)) / 3
S = (4/3) (s - s/4^(n+1))
S = (4/3) (s - s/(1<<(2n+2)))
S = (4/3) (s - s>>(2n+2))
*/
return (size - C3D_TexCalcLevelSize(size,maxLevel+1)) * 4 / 3;
}
static inline bool C3D_TexInit(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format)
{
return C3D_TexInitWithParams(tex,
@ -64,6 +89,13 @@ static inline bool C3D_TexInitVRAM(C3D_Tex* tex, u16 width, u16 height, GPU_TEXC
(C3D_TexInitParams){ width, height, 0, format, GPU_TEX_2D, true });
}
static inline void* C3D_TexGetLevel(C3D_Tex* tex, int level, u32* size)
{
if (size) *size = C3D_TexCalcLevelSize(tex->size, level);
if (!level) return tex->data;
return (u8*)tex->data + C3D_TexCalcTotalSize(tex->size, level-1);
}
static inline void C3D_TexUpload(C3D_Tex* tex, const void* data)
{
C3D_TexUploadLevel(tex, data, 0);

View File

@ -43,19 +43,9 @@ bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexInitParams p)
u32 size = fmtSize(p.format);
if (!size) return false;
size *= (u32)p.width * p.height / 8;
u32 total_size = C3D_TexCalcLevelSize(size, p.maxLevel);
u32 alloc_size = size;
{
int i;
u32 level_size = size;
for (i = 0; i < p.maxLevel; i ++)
{
level_size >>= 2;
alloc_size += level_size;
}
}
tex->data = p.onVram ? vramAlloc(alloc_size) : linearAlloc(alloc_size);
tex->data = p.onVram ? vramAlloc(total_size) : linearAlloc(total_size);
if (!tex->data) return false;
tex->width = p.width;
@ -74,24 +64,6 @@ bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexInitParams p)
return true;
}
void* C3D_TexGetLevel(C3D_Tex* tex, int level, u32* size)
{
u8* data = (u8*)tex->data;
u32 level_size = tex->size;
int i;
for (i = 0; i <= tex->maxLevel; i ++)
{
if (i == level)
{
if (size) *size = level_size;
return data;
}
data += level_size;
level_size >>= 2;
}
return NULL;
}
void C3D_TexUploadLevel(C3D_Tex* tex, const void* data, int level)
{
if (!tex->data || addrIsVRAM(tex->data))
@ -99,8 +71,7 @@ void C3D_TexUploadLevel(C3D_Tex* tex, const void* data, int level)
u32 size = 0;
void* out = C3D_TexGetLevel(tex, level, &size);
if (out)
memcpy(out, data, size);
memcpy(out, data, size);
}
static void C3Di_DownscaleRGBA8(u32* dst, const u32* src[4])