Add support for cube textures, see below:

- Added C3D_TexCube structure for holding pointers to all 6 faces
- C3D_TexLoadImage accepts face number and mipmap level
- C3D_TexGenerateMipmap accepts face number
- Added functions (C3D_Tex*GetImagePtr) for accessing image data
- Now using GX to copy texture data to VRAM instead of failing
- Moved texunit setup code to texture.c
This commit is contained in:
fincs 2017-02-12 00:14:04 +01:00
parent 6ec42ae8a1
commit 15de0e300e
4 changed files with 170 additions and 55 deletions

View File

@ -3,12 +3,30 @@
typedef struct typedef struct
{ {
void* data; void* data[6];
} C3D_TexCube;
typedef struct
{
union
{
void* data;
C3D_TexCube* cube;
};
GPU_TEXCOLOR fmt : 4; GPU_TEXCOLOR fmt : 4;
size_t size : 28; size_t size : 28;
u16 width, height; union
{
u32 dim;
struct
{
u16 height;
u16 width;
};
};
u32 param; u32 param;
u32 border; u32 border;
union union
@ -33,9 +51,9 @@ typedef struct ALIGN(8)
bool onVram : 1; bool onVram : 1;
} C3D_TexInitParams; } C3D_TexInitParams;
bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexInitParams p); bool C3D_TexInitWithParams(C3D_Tex* tex, C3D_TexCube* cube, C3D_TexInitParams p);
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);
void C3D_TexGenerateMipmap(C3D_Tex* tex); void C3D_TexGenerateMipmap(C3D_Tex* tex, GPU_TEXFACE face);
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);
@ -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) 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 }); (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) 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 }); (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) 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 }); (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); return (GPU_TEXTURE_MODE_PARAM)((tex->param>>28)&0x7);
if (!level) return tex->data; }
return (u8*)tex->data + C3D_TexCalcTotalSize(tex->size, level-1);
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) 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) static inline void C3D_TexSetFilter(C3D_Tex* tex, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter)

View File

@ -5,32 +5,6 @@
C3D_Context __C3D_Context; 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 aptHookCookie hookCookie;
static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param) static void C3Di_AptEventHook(APT_HookType hookType, C3D_UNUSED void* param)

View File

@ -88,6 +88,7 @@ void C3Di_UpdateContext(void);
void C3Di_AttrInfoBind(C3D_AttrInfo* info); void C3Di_AttrInfoBind(C3D_AttrInfo* info);
void C3Di_BufInfoBind(C3D_BufInfo* info); void C3Di_BufInfoBind(C3D_BufInfo* info);
void C3Di_TexEnvBind(int id, C3D_TexEnv* env); 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_EffectBind(C3D_Effect* effect);
void C3Di_RenderBufBind(C3D_RenderBuf* rb); void C3Di_RenderBufBind(C3D_RenderBuf* rb);

View File

@ -1,4 +1,5 @@
#include "internal.h" #include "internal.h"
#include <c3d/renderqueue.h>
// Return bits per pixel // Return bits per pixel
static inline size_t fmtSize(GPU_TEXCOLOR fmt) 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) static inline bool addrIsVRAM(const void* addr)
{ {
u32 vaddr = (u32)addr; u32 vaddr = (u32)addr;
return vaddr >= 0x1F000000 && vaddr < 0x1F600000; 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 (tex->data) return false;
if ((p.width|p.height) & 7) 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); u32 size = fmtSize(p.format);
if (!size) return false; if (!size) return false;
size *= (u32)p.width * p.height / 8; size *= (u32)p.width * p.height / 8;
u32 total_size = C3D_TexCalcLevelSize(size, p.maxLevel); u32 total_size = C3D_TexCalcLevelSize(size, p.maxLevel);
tex->data = p.onVram ? vramAlloc(total_size) : linearAlloc(total_size); if (!isCube)
if (!tex->data) return false; {
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->width = p.width;
tex->height = p.height; 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) if (p.format == GPU_ETC1)
tex->param |= GPU_TEXTURE_ETC1_PARAM; tex->param |= GPU_TEXTURE_ETC1_PARAM;
if (p.type == GPU_TEX_SHADOW_2D || p.type == GPU_TEX_SHADOW_CUBE) 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; 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; return;
u32 size = 0; u32 size = 0;
void* out = C3D_TexGetLevel(tex, level, &size); void* out = C3D_TexGetImagePtr(tex,
memcpy(out, data, size); 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]) 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; int fmt = tex->fmt;
size_t block_size = (8*8*fmtSize(fmt))/8; 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); 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; int i;
u32 level_size = tex->size; u32 level_size = tex->size;
void* src = tex->data;
u32 src_width = tex->width; u32 src_width = tex->width;
u32 src_height = tex->height; u32 src_height = tex->height;
for (i = 0; i < tex->maxLevel; i ++) for (i = 0; i < tex->maxLevel; i ++)
@ -176,7 +241,7 @@ 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) if (unitId > 0 && C3D_TexGetType(tex) != GPU_TEX_2D)
return; return;
ctx->flags |= C3DiF_Tex(unitId); ctx->flags |= C3DiF_Tex(unitId);
@ -186,17 +251,53 @@ void C3D_TexBind(int unitId, C3D_Tex* tex)
void C3D_TexFlush(C3D_Tex* tex) void C3D_TexFlush(C3D_Tex* tex)
{ {
if (tex->data && !addrIsVRAM(tex->data)) 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) void C3D_TexDelete(C3D_Tex* tex)
{ {
if (!tex->data) return; if (!tex->data) return;
if (addrIsVRAM(tex->data)) if (C3Di_TexIs2D(tex))
vramFree(tex->data); allocFree(tex->data);
else else
linearFree(tex->data); C3Di_TexCubeDelete(tex->cube);
tex->data = NULL; 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;
}
}