diff --git a/include/c3d/texture.h b/include/c3d/texture.h index a9d743c..33200ad 100644 --- a/include/c3d/texture.h +++ b/include/c3d/texture.h @@ -10,13 +10,89 @@ typedef struct u16 width, height; u32 param; + u32 border; + union + { + u32 lodParam; + struct + { + u16 lodBias; + u8 maxLevel; + u8 minLevel; + }; + }; } C3D_Tex; -bool C3D_TexInit(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format); -bool C3D_TexInitVRAM(C3D_Tex* tex, int width, int height, GPU_TEXCOLOR format); -void C3D_TexUpload(C3D_Tex* tex, const void* data); -void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter); -void C3D_TexSetWrap(C3D_Tex* tex, GPU_TEXTURE_WRAP_PARAM wrapS, GPU_TEXTURE_WRAP_PARAM wrapT); +typedef struct ALIGN(8) +{ + u16 width; + u16 height; + 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_TexFlush(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; +} diff --git a/source/base.c b/source/base.c index 6c363fe..ebed4b8 100644 --- a/source/base.c +++ b/source/base.c @@ -7,31 +7,26 @@ C3D_Context __C3D_Context; static void C3Di_SetTex(GPU_TEXUNIT unit, C3D_Tex* tex) { - u32 reg[4]; - reg[0] = tex->fmt; - reg[1] = osConvertVirtToPhys(tex->data) >> 3; - reg[2] = (u32)tex->height | ((u32)tex->width << 16); - reg[3] = tex->param; + u32 reg[5]; + reg[0] = tex->border; + reg[1] = (u32)tex->height | ((u32)tex->width << 16); + reg[2] = tex->param; + reg[3] = tex->lodParam; + reg[4] = osConvertVirtToPhys(tex->data) >> 3; switch (unit) { case GPU_TEXUNIT0: - GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, reg[0]); - GPUCMD_AddWrite(GPUREG_TEXUNIT0_ADDR1, reg[1]); - GPUCMD_AddWrite(GPUREG_TEXUNIT0_DIM, reg[2]); - GPUCMD_AddWrite(GPUREG_TEXUNIT0_PARAM, reg[3]); + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT0_BORDER_COLOR, reg, 5); + GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, tex->fmt); break; case GPU_TEXUNIT1: - GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, reg[0]); - GPUCMD_AddWrite(GPUREG_TEXUNIT1_ADDR, reg[1]); - GPUCMD_AddWrite(GPUREG_TEXUNIT1_DIM, reg[2]); - GPUCMD_AddWrite(GPUREG_TEXUNIT1_PARAM, reg[3]); + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT1_BORDER_COLOR, reg, 5); + GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, tex->fmt); break; case GPU_TEXUNIT2: - GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, reg[0]); - GPUCMD_AddWrite(GPUREG_TEXUNIT2_ADDR, reg[1]); - GPUCMD_AddWrite(GPUREG_TEXUNIT2_DIM, reg[2]); - GPUCMD_AddWrite(GPUREG_TEXUNIT2_PARAM, reg[3]); + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT2_BORDER_COLOR, reg, 5); + GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, tex->fmt); break; } } @@ -99,6 +94,7 @@ bool C3D_Init(size_t cmdBufSize) 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_FragOpMode(GPU_FRAGOPMODE_GL); + C3D_FragOpShadow(0.0, 1.0); ctx->texEnvBuf = 0; ctx->texEnvBufClr = 0xFFFFFFFF; diff --git a/source/texture.c b/source/texture.c index 58cdbbc..94e5534 100644 --- a/source/texture.c +++ b/source/texture.c @@ -35,53 +35,167 @@ static inline bool addrIsVRAM(const void* addr) 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 ((p.width|p.height) & 7) return false; - u32 size = fmtSize(format); + u32 size = fmtSize(p.format); 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; - tex->width = width; - tex->height = height; - tex->param = GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST); - if (format == GPU_ETC1) + tex->width = p.width; + tex->height = p.height; + tex->param = GPU_TEXTURE_MAG_FILTER(GPU_NEAREST) | GPU_TEXTURE_MIN_FILTER(GPU_NEAREST) | GPU_TEXTURE_MODE(p.type); + if (p.format == GPU_ETC1) 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->border = 0; + tex->lodBias = 0; + tex->maxLevel = p.maxLevel; + tex->minLevel = 0; 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)) - memcpy(tex->data, data, tex->size); + u32 i, j; + 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<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); + u32 i, j; + 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)); - tex->param |= GPU_TEXTURE_WRAP_S(wrapS) | GPU_TEXTURE_WRAP_T(wrapT); + int fmt = tex->fmt; + 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) @@ -91,6 +205,9 @@ void C3D_TexBind(int unitId, C3D_Tex* tex) if (!(ctx->flags & C3DiF_Active)) return; + if (unitId > 0 && ((tex->param>>28)&7) != GPU_TEX_2D) + return; + ctx->flags |= C3DiF_Tex(unitId); ctx->tex[unitId] = tex; }