diff --git a/include/c3d/texture.h b/include/c3d/texture.h index d44e02b..9db4830 100644 --- a/include/c3d/texture.h +++ b/include/c3d/texture.h @@ -3,12 +3,30 @@ typedef struct { - void* data; + void* data[6]; +} C3D_TexCube; + +typedef struct +{ + union + { + void* data; + C3D_TexCube* cube; + }; GPU_TEXCOLOR fmt : 4; size_t size : 28; - u16 width, height; + union + { + u32 dim; + struct + { + u16 height; + u16 width; + }; + }; + u32 param; u32 border; union @@ -33,9 +51,9 @@ typedef struct ALIGN(8) bool onVram : 1; } C3D_TexInitParams; -bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexInitParams p); -void C3D_TexUploadLevel(C3D_Tex* tex, const void* data, int level); -void C3D_TexGenerateMipmap(C3D_Tex* tex); +bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexCube* cube, C3D_TexInitParams p); +void C3D_TexLoadImage(C3D_Tex* tex, const void* data, GPU_TEXFACE face, int level); +void C3D_TexGenerateMipmap(C3D_Tex* tex, GPU_TEXFACE face); void C3D_TexBind(int unitId, C3D_Tex* tex); void C3D_TexFlush(C3D_Tex* tex); void C3D_TexDelete(C3D_Tex* tex); @@ -73,32 +91,53 @@ static inline u32 C3D_TexCalcTotalSize(u32 size, int maxLevel) static inline bool C3D_TexInit(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format) { - return C3D_TexInitWithParams(tex, + return C3D_TexInitWithParams(tex, NULL, (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, + return C3D_TexInitWithParams(tex, NULL, (C3D_TexInitParams){ width, height, (u8)C3D_TexCalcMaxLevel(width, height), format, GPU_TEX_2D, false }); } +static inline bool C3D_TexInitCube(C3D_Tex* tex, C3D_TexCube* cube, u16 width, u16 height, GPU_TEXCOLOR format) +{ + return C3D_TexInitWithParams(tex, cube, + (C3D_TexInitParams){ width, height, 0, format, GPU_TEX_CUBE_MAP, false }); +} + static inline bool C3D_TexInitVRAM(C3D_Tex* tex, u16 width, u16 height, GPU_TEXCOLOR format) { - return C3D_TexInitWithParams(tex, + return C3D_TexInitWithParams(tex, NULL, (C3D_TexInitParams){ width, height, 0, format, GPU_TEX_2D, true }); } -static inline void* C3D_TexGetLevel(C3D_Tex* tex, int level, u32* size) +static inline GPU_TEXTURE_MODE_PARAM C3D_TexGetType(C3D_Tex* tex) { - if (size) *size = C3D_TexCalcLevelSize(tex->size, level); - if (!level) return tex->data; - return (u8*)tex->data + C3D_TexCalcTotalSize(tex->size, level-1); + return (GPU_TEXTURE_MODE_PARAM)((tex->param>>28)&0x7); +} + +static inline void* C3D_TexGetImagePtr(C3D_Tex* tex, void* data, int level, u32* size) +{ + if (size) *size = level > 0 ? C3D_TexCalcLevelSize(tex->size, level) : C3D_TexCalcTotalSize(tex->size, tex->maxLevel); + if (!level) return data; + return (u8*)data + (level > 0 ? C3D_TexCalcTotalSize(tex->size, level-1) : 0); +} + +static inline void* C3D_Tex2DGetImagePtr(C3D_Tex* tex, int level, u32* size) +{ + return C3D_TexGetImagePtr(tex, tex->data, level, size); +} + +static inline void* C3D_TexCubeGetImagePtr(C3D_Tex* tex, GPU_TEXFACE face, int level, u32* size) +{ + return C3D_TexGetImagePtr(tex, tex->cube->data[face], level, size); } static inline void C3D_TexUpload(C3D_Tex* tex, const void* data) { - C3D_TexUploadLevel(tex, data, 0); + C3D_TexLoadImage(tex, data, GPU_TEXFACE_2D, 0); } static inline void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter) diff --git a/source/base.c b/source/base.c index ebed4b8..a62802b 100644 --- a/source/base.c +++ b/source/base.c @@ -5,32 +5,6 @@ C3D_Context __C3D_Context; -static void C3Di_SetTex(GPU_TEXUNIT unit, C3D_Tex* tex) -{ - 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_AddIncrementalWrites(GPUREG_TEXUNIT0_BORDER_COLOR, reg, 5); - GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, tex->fmt); - break; - case GPU_TEXUNIT1: - GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT1_BORDER_COLOR, reg, 5); - GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, tex->fmt); - break; - case GPU_TEXUNIT2: - GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT2_BORDER_COLOR, reg, 5); - GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, tex->fmt); - break; - } -} - static aptHookCookie hookCookie; static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param) diff --git a/source/internal.h b/source/internal.h index da1f7cd..1be24c1 100644 --- a/source/internal.h +++ b/source/internal.h @@ -88,6 +88,7 @@ void C3Di_UpdateContext(void); void C3Di_AttrInfoBind(C3D_AttrInfo* info); void C3Di_BufInfoBind(C3D_BufInfo* info); void C3Di_TexEnvBind(int id, C3D_TexEnv* env); +void C3Di_SetTex(GPU_TEXUNIT unit, C3D_Tex* tex); void C3Di_EffectBind(C3D_Effect* effect); void C3Di_RenderBufBind(C3D_RenderBuf* rb); diff --git a/source/texture.c b/source/texture.c index f6078d0..4e33e07 100644 --- a/source/texture.c +++ b/source/texture.c @@ -1,4 +1,5 @@ #include "internal.h" +#include // Return bits per pixel static inline size_t fmtSize(GPU_TEXCOLOR fmt) @@ -29,28 +30,80 @@ static inline size_t fmtSize(GPU_TEXCOLOR fmt) } } +static inline bool typeIsCube(GPU_TEXTURE_MODE_PARAM type) +{ + return type == GPU_TEX_CUBE_MAP || type == GPU_TEX_SHADOW_CUBE; +} + +static inline bool C3Di_TexIs2D(C3D_Tex* tex) +{ + return !typeIsCube(C3D_TexGetType(tex)); +} + static inline bool addrIsVRAM(const void* addr) { u32 vaddr = (u32)addr; return vaddr >= 0x1F000000 && vaddr < 0x1F600000; } -bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexInitParams p) +static inline void allocFree(void* addr) +{ + if (addrIsVRAM(addr)) + vramFree(addr); + else + linearFree(addr); +} + +static void C3Di_TexCubeDelete(C3D_TexCube* cube) +{ + int i; + for (i = 0; i < 6; i ++) + { + if (cube->data[i]) + { + allocFree(cube->data[i]); + cube->data[i] = NULL; + } + } +} + +bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexCube* cube, C3D_TexInitParams p) { if (tex->data) return false; if ((p.width|p.height) & 7) return false; + bool isCube = typeIsCube(p.type); + if (isCube && !cube) return false; + u32 size = fmtSize(p.format); if (!size) return false; size *= (u32)p.width * p.height / 8; u32 total_size = C3D_TexCalcLevelSize(size, p.maxLevel); - tex->data = p.onVram ? vramAlloc(total_size) : linearAlloc(total_size); - if (!tex->data) return false; + if (!isCube) + { + tex->data = p.onVram ? vramAlloc(total_size) : linearAlloc(total_size); + if (!tex->data) return false; + } else + { + memset(cube, 0, sizeof(*cube)); + int i; + for (i = 0; i < 6; i ++) + { + cube->data[i] = p.onVram ? vramAlloc(total_size) : linearAlloc(total_size); + if (!cube->data[i] || + (i>0 && (((u32)cube->data[0] ^ (u32)cube->data[i])>>(3+22)))) // Check upper 6bits match with first face + { + C3Di_TexCubeDelete(cube); + return false; + } + } + tex->cube = cube; + } 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); + tex->param = GPU_TEXTURE_MODE(p.type); if (p.format == GPU_ETC1) tex->param |= GPU_TEXTURE_ETC1_PARAM; if (p.type == GPU_TEX_SHADOW_2D || p.type == GPU_TEX_SHADOW_CUBE) @@ -64,14 +117,23 @@ bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexInitParams p) return true; } -void C3D_TexUploadLevel(C3D_Tex* tex, const void* data, int level) +void C3D_TexLoadImage(C3D_Tex* tex, const void* data, GPU_TEXFACE face, int level) { - if (!tex->data || addrIsVRAM(tex->data)) + if (!tex->data) return; u32 size = 0; - void* out = C3D_TexGetLevel(tex, level, &size); - memcpy(out, data, size); + void* out = C3D_TexGetImagePtr(tex, + C3Di_TexIs2D(tex) ? tex->data : tex->cube->data[face], + level, &size); + + if (!addrIsVRAM(out)) + memcpy(out, data, size); + else + { + C3D_SafeTextureCopy((u32*)data, 0, (u32*)out, 0, size, 8); + gspWaitForPPF(); + } } static void C3Di_DownscaleRGBA8(u32* dst, const u32* src[4]) @@ -104,7 +166,7 @@ static void C3Di_DownscaleRGB8(u8* dst, const u8* src[4]) } } -void C3D_TexGenerateMipmap(C3D_Tex* tex) +void C3D_TexGenerateMipmap(C3D_Tex* tex, GPU_TEXFACE face) { int fmt = tex->fmt; size_t block_size = (8*8*fmtSize(fmt))/8; @@ -115,9 +177,12 @@ void C3D_TexGenerateMipmap(C3D_Tex* tex) GX_TRANSFER_IN_FORMAT(tex->fmt) | GX_TRANSFER_OUT_FORMAT(tex->fmt); */ + void* src = C3Di_TexIs2D(tex) ? tex->data : tex->cube->data[face]; + if (addrIsVRAM(src)) + return; // CPU can't write to VRAM + 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 ++) @@ -176,7 +241,7 @@ void C3D_TexBind(int unitId, C3D_Tex* tex) if (!(ctx->flags & C3DiF_Active)) return; - if (unitId > 0 && ((tex->param>>28)&7) != GPU_TEX_2D) + if (unitId > 0 && C3D_TexGetType(tex) != GPU_TEX_2D) return; ctx->flags |= C3DiF_Tex(unitId); @@ -186,17 +251,53 @@ void C3D_TexBind(int unitId, C3D_Tex* tex) void C3D_TexFlush(C3D_Tex* tex) { if (tex->data && !addrIsVRAM(tex->data)) - GSPGPU_FlushDataCache(tex->data, tex->size); + GSPGPU_FlushDataCache(tex->data, C3D_TexCalcTotalSize(tex->size, tex->maxLevel)); } void C3D_TexDelete(C3D_Tex* tex) { if (!tex->data) return; - if (addrIsVRAM(tex->data)) - vramFree(tex->data); + if (C3Di_TexIs2D(tex)) + allocFree(tex->data); else - linearFree(tex->data); - + C3Di_TexCubeDelete(tex->cube); tex->data = NULL; } + +void C3Di_SetTex(GPU_TEXUNIT unit, C3D_Tex* tex) +{ + u32 reg[10]; + u32 regcount = 5; + reg[0] = tex->border; + reg[1] = tex->dim; + reg[2] = tex->param; + reg[3] = tex->lodParam; + if (C3Di_TexIs2D(tex)) + reg[4] = osConvertVirtToPhys(tex->data) >> 3; + else + { + int i; + C3D_TexCube* cube = tex->cube; + regcount = 10; + reg[4] = osConvertVirtToPhys(cube->data[0]) >> 3; + for (i = 1; i < 6; i ++) + reg[4+i] = (osConvertVirtToPhys(cube->data[i]) >> 3) & 0x3FFFFF; + } + + switch (unit) + { + case GPU_TEXUNIT0: + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT0_BORDER_COLOR, reg, regcount); + GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, tex->fmt); + break; + case GPU_TEXUNIT1: + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT1_BORDER_COLOR, reg, 5); + GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, tex->fmt); + break; + case GPU_TEXUNIT2: + GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT2_BORDER_COLOR, reg, 5); + GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, tex->fmt); + break; + } +}