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:
parent
2a1dc2ba87
commit
0fd245b171
@ -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;
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
161
source/texture.c
161
source/texture.c
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user