Begin overhauling texture API, see details:

- Now supports multilevel data (LOD) used for mipmapping
- Basic mipmap generation (currently only for RGBA8 and RGB8 textures)
- Texture type can now be specified (TODO: support cube textures)
- Border color can also be specified now
- More flexibility in texture initialization parameters
This commit is contained in:
fincs 2017-02-09 20:07:37 +01:00
parent 2a1dc2ba87
commit 0fd245b171
3 changed files with 233 additions and 44 deletions

View File

@ -10,13 +10,89 @@ typedef struct
u16 width, height; u16 width, height;
u32 param; u32 param;
u32 border;
union
{
u32 lodParam;
struct
{
u16 lodBias;
u8 maxLevel;
u8 minLevel;
};
};
} C3D_Tex; } C3D_Tex;
bool C3D_TexInit(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format); typedef struct ALIGN(8)
bool C3D_TexInitVRAM(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format); {
void C3D_TexUpload(C3D_Tex* tex, const void* data); u16 width;
void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter); u16 height;
void C3D_TexSetWrap(C3D_Tex* tex, GPU_TEXTURE_WRAP_PARAM wrapS, GPU_TEXTURE_WRAP_PARAM wrapT); u8 maxLevel : 4;
GPU_TEXCOLOR format : 4;
GPU_TEXTURE_MODE_PARAM type : 3;
bool onVram : 1;
} 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); void C3D_TexBind(int unitId, C3D_Tex* tex);
void C3D_TexFlush(C3D_Tex* tex); void C3D_TexFlush(C3D_Tex* tex);
void C3D_TexDelete(C3D_Tex* tex); void C3D_TexDelete(C3D_Tex* tex);
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 bool C3D_TexInit(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format)
{
return C3D_TexInitWithParams(tex,
(C3D_TexInitParams){ width, height, 0, format, GPU_TEX_2D, false });
}
static inline bool C3D_TexInitMipmap(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format)
{
return C3D_TexInitWithParams(tex,
(C3D_TexInitParams){ width, height, (u8)C3D_TexCalcMaxLevel(width, height), format, GPU_TEX_2D, false });
}
static inline bool C3D_TexInitVRAM(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format)
{
return C3D_TexInitWithParams(tex,
(C3D_TexInitParams){ width, height, 0, format, GPU_TEX_2D, true });
}
static inline void C3D_TexUpload(C3D_Tex* tex, const void* data)
{
C3D_TexUploadLevel(tex, data, 0);
}
static inline void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter)
{
tex->param &= ~(GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR));
tex->param |= GPU_TEXTURE_MAG_FILTER(magFilter) | GPU_TEXTURE_MIN_FILTER(minFilter);
}
static inline void C3D_TexSetFilterMipmap(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM filter)
{
tex->param &= ~GPU_TEXTURE_MIP_FILTER(GPU_LINEAR);
tex->param |= GPU_TEXTURE_MIP_FILTER(filter);
}
static inline void C3D_TexSetWrap(C3D_Tex* tex, GPU_TEXTURE_WRAP_PARAM wrapS, GPU_TEXTURE_WRAP_PARAM wrapT)
{
tex->param &= ~(GPU_TEXTURE_WRAP_S(3) | GPU_TEXTURE_WRAP_T(3));
tex->param |= GPU_TEXTURE_WRAP_S(wrapS) | GPU_TEXTURE_WRAP_T(wrapT);
}
static inline void C3D_TexSetLodBias(C3D_Tex* tex, float lodBias)
{
int iLodBias = (int)(lodBias*0x100);
if (iLodBias > 0xFFF)
iLodBias = 0xFFF;
else if (iLodBias < -0x1000)
iLodBias = -0x1000;
tex->lodBias = iLodBias & 0x1FFF;
}

View File

@ -7,31 +7,26 @@ C3D_Context __C3D_Context;
static void C3Di_SetTex(GPU_TEXUNIT unit, C3D_Tex* tex) static void C3Di_SetTex(GPU_TEXUNIT unit, C3D_Tex* tex)
{ {
u32 reg[4]; u32 reg[5];
reg[0] = tex->fmt; reg[0] = tex->border;
reg[1] = osConvertVirtToPhys(tex->data) >> 3; reg[1] = (u32)tex->height | ((u32)tex->width << 16);
reg[2] = (u32)tex->height | ((u32)tex->width << 16); reg[2] = tex->param;
reg[3] = tex->param; reg[3] = tex->lodParam;
reg[4] = osConvertVirtToPhys(tex->data) >> 3;
switch (unit) switch (unit)
{ {
case GPU_TEXUNIT0: case GPU_TEXUNIT0:
GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, reg[0]); GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT0_BORDER_COLOR, reg, 5);
GPUCMD_AddWrite(GPUREG_TEXUNIT0_ADDR1, reg[1]); GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, tex->fmt);
GPUCMD_AddWrite(GPUREG_TEXUNIT0_DIM, reg[2]);
GPUCMD_AddWrite(GPUREG_TEXUNIT0_PARAM, reg[3]);
break; break;
case GPU_TEXUNIT1: case GPU_TEXUNIT1:
GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, reg[0]); GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT1_BORDER_COLOR, reg, 5);
GPUCMD_AddWrite(GPUREG_TEXUNIT1_ADDR, reg[1]); GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, tex->fmt);
GPUCMD_AddWrite(GPUREG_TEXUNIT1_DIM, reg[2]);
GPUCMD_AddWrite(GPUREG_TEXUNIT1_PARAM, reg[3]);
break; break;
case GPU_TEXUNIT2: case GPU_TEXUNIT2:
GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, reg[0]); GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT2_BORDER_COLOR, reg, 5);
GPUCMD_AddWrite(GPUREG_TEXUNIT2_ADDR, reg[1]); GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, tex->fmt);
GPUCMD_AddWrite(GPUREG_TEXUNIT2_DIM, reg[2]);
GPUCMD_AddWrite(GPUREG_TEXUNIT2_PARAM, reg[3]);
break; break;
} }
} }
@ -99,6 +94,7 @@ bool C3D_Init(size_t cmdBufSize)
C3D_AlphaTest(false, GPU_ALWAYS, 0x00); C3D_AlphaTest(false, GPU_ALWAYS, 0x00);
C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA); C3D_AlphaBlend(GPU_BLEND_ADD, GPU_BLEND_ADD, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA);
C3D_FragOpMode(GPU_FRAGOPMODE_GL); C3D_FragOpMode(GPU_FRAGOPMODE_GL);
C3D_FragOpShadow(0.0, 1.0);
ctx->texEnvBuf = 0; ctx->texEnvBuf = 0;
ctx->texEnvBufClr = 0xFFFFFFFF; ctx->texEnvBufClr = 0xFFFFFFFF;

View File

@ -35,53 +35,167 @@ static inline bool addrIsVRAM(const void* addr)
return vaddr >= 0x1F000000 && vaddr < 0x1F600000; return vaddr >= 0x1F000000 && vaddr < 0x1F600000;
} }
static bool C3Di_TexInitCommon(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format, void* (*texAlloc)(size_t)) bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexInitParams p)
{ {
if (tex->data) return false; if (tex->data) return false;
if ((p.width|p.height) & 7) return false;
u32 size = fmtSize(format); u32 size = fmtSize(p.format);
if (!size) return false; if (!size) return false;
size *= width * height / 8; size *= (u32)p.width * p.height / 8;
tex->data = texAlloc(size); 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);
if (!tex->data) return false; if (!tex->data) return false;
tex->width = width; tex->width = p.width;
tex->height = height; tex->height = p.height;
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST); tex->param = GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST) | GPU_TEXTURE_MODE(p.type);
if (format == GPU_ETC1) if (p.format == GPU_ETC1)
tex->param |= GPU_TEXTURE_ETC1_PARAM; tex->param |= GPU_TEXTURE_ETC1_PARAM;
tex->fmt = format; if (p.type == GPU_TEX_SHADOW_2D || p.type == GPU_TEX_SHADOW_CUBE)
tex->param |= GPU_TEXTURE_SHADOW_PARAM;
tex->fmt = p.format;
tex->size = size; tex->size = size;
tex->border = 0;
tex->lodBias = 0;
tex->maxLevel = p.maxLevel;
tex->minLevel = 0;
return true; return true;
} }
bool C3D_TexInit(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format) void* C3D_TexGetLevel(C3D_Tex* tex, int level, u32* size)
{ {
return C3Di_TexInitCommon(tex, width, height, format, linearAlloc); 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;
} }
bool C3D_TexInitVRAM(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format) void C3D_TexUploadLevel(C3D_Tex* tex, const void* data, int level)
{ {
return C3Di_TexInitCommon(tex, width, height, format, vramAlloc); if (!tex->data || addrIsVRAM(tex->data))
return;
u32 size = 0;
void* out = C3D_TexGetLevel(tex, level, &size);
if (out)
memcpy(out, data, size);
} }
void C3D_TexUpload(C3D_Tex* tex, const void* data) static void C3Di_DownscaleRGBA8(u32* dst, const u32* src[4])
{ {
if (tex->data && !addrIsVRAM(tex->data)) u32 i, j;
memcpy(tex->data, data, tex->size); for (i = 0; i < 64; i ++)
{
const u32* a = src[i>>4] + (i<<2 & 0x3F);
u32 dest = 0;
for (j = 0; j < 32; j += 8)
{
u32 val = (((a[0]>>j)&0xFF)+((a[1]>>j)&0xFF)+((a[2]>>j)&0xFF)+((a[3]>>j)&0xFF))>>2;
dest |= val<<j;
}
*dst++ = dest;
}
} }
void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter) static void C3Di_DownscaleRGB8(u8* dst, const u8* src[4])
{ {
tex->param &= ~(GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)); u32 i, j;
tex->param |= GPU_TEXTURE_MAG_FILTER(magFilter) | GPU_TEXTURE_MIN_FILTER(minFilter); for (i = 0; i < 64; i ++)
{
const u8* a = src[i>>4] + 3*(i<<2 & 0x3F);
for (j = 0; j < 3; j ++)
{
*dst++ = ((u32)a[0] + a[3] + a[6] + a[9])>>2;
a++;
}
}
} }
void C3D_TexSetWrap(C3D_Tex* tex, GPU_TEXTURE_WRAP_PARAM wrapS, GPU_TEXTURE_WRAP_PARAM wrapT) void C3D_TexGenerateMipmap(C3D_Tex* tex)
{ {
tex->param &= ~(GPU_TEXTURE_WRAP_S(3) | GPU_TEXTURE_WRAP_T(3)); int fmt = tex->fmt;
tex->param |= GPU_TEXTURE_WRAP_S(wrapS) | GPU_TEXTURE_WRAP_T(wrapT); size_t block_size = (8*8*fmtSize(fmt))/8;
/*
const u32 transfer_flags =
GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_XY) | BIT(5) |
GX_TRANSFER_IN_FORMAT(tex->fmt) | GX_TRANSFER_OUT_FORMAT(tex->fmt);
*/
int i;
u32 level_size = tex->size;
void* src = tex->data;
u32 src_width = tex->width;
u32 src_height = tex->height;
for (i = 0; i < tex->maxLevel; i ++)
{
void* dst = (u8*)src + level_size;
u32 dst_width = src_width>>1;
u32 dst_height = src_height>>1;
/* Doesn't work due to size restriction bullshit
C3D_SafeDisplayTransfer(
(u32*)src, GX_BUFFER_DIM(src_width,src_height),
(u32*)dst, GX_BUFFER_DIM(dst_width,dst_height),
transfer_flags);
gspWaitForPPF();
*/
u32 i,j;
u32 src_stride = src_width/8;
u32 dst_stride = dst_width/8;
for (j = 0; j < (dst_height/8); j ++)
{
for (i = 0; i < dst_stride; i ++)
{
void* dst_block = (u8*)dst + block_size*(i + j*dst_stride);
const void* src_blocks[4] =
{
(u8*)src + block_size*(2*i+0 + (2*j+0)*src_stride),
(u8*)src + block_size*(2*i+1 + (2*j+0)*src_stride),
(u8*)src + block_size*(2*i+0 + (2*j+1)*src_stride),
(u8*)src + block_size*(2*i+1 + (2*j+1)*src_stride),
};
switch (fmt)
{
case GPU_RGBA8:
C3Di_DownscaleRGBA8(dst_block, (const u32**)src_blocks);
break;
case GPU_RGB8:
C3Di_DownscaleRGB8(dst_block, (const u8**)src_blocks);
default:
break;
}
}
}
level_size >>= 2;
src = dst;
src_width = dst_width;
src_height = dst_height;
}
} }
void C3D_TexBind(int unitId, C3D_Tex* tex) void C3D_TexBind(int unitId, C3D_Tex* tex)
@ -91,6 +205,9 @@ void C3D_TexBind(int unitId, C3D_Tex* tex)
if (!(ctx->flags & C3DiF_Active)) if (!(ctx->flags & C3DiF_Active))
return; return;
if (unitId > 0 && ((tex->param>>28)&7) != GPU_TEX_2D)
return;
ctx->flags |= C3DiF_Tex(unitId); ctx->flags |= C3DiF_Tex(unitId);
ctx->tex[unitId] = tex; ctx->tex[unitId] = tex;
} }