2017-02-09 04:30:25 +01:00
|
|
|
#include "internal.h"
|
2017-02-12 00:14:04 +01:00
|
|
|
#include <c3d/renderqueue.h>
|
2014-12-20 21:34:19 +01:00
|
|
|
|
2016-05-03 08:41:05 +02:00
|
|
|
// Return bits per pixel
|
2014-12-20 21:34:19 +01:00
|
|
|
static inline size_t fmtSize(GPU_TEXCOLOR fmt)
|
|
|
|
{
|
|
|
|
switch (fmt)
|
|
|
|
{
|
|
|
|
case GPU_RGBA8:
|
2016-05-03 08:41:05 +02:00
|
|
|
return 32;
|
2014-12-20 21:34:19 +01:00
|
|
|
case GPU_RGB8:
|
2016-05-03 08:41:05 +02:00
|
|
|
return 24;
|
2014-12-20 21:34:19 +01:00
|
|
|
case GPU_RGBA5551:
|
|
|
|
case GPU_RGB565:
|
|
|
|
case GPU_RGBA4:
|
2016-04-04 00:10:06 +02:00
|
|
|
case GPU_LA8:
|
|
|
|
case GPU_HILO8:
|
2016-05-03 08:41:05 +02:00
|
|
|
return 16;
|
2016-04-04 00:10:06 +02:00
|
|
|
case GPU_L8:
|
|
|
|
case GPU_A8:
|
|
|
|
case GPU_LA4:
|
2016-05-03 08:41:05 +02:00
|
|
|
case GPU_ETC1A4:
|
|
|
|
return 8;
|
|
|
|
case GPU_L4:
|
|
|
|
case GPU_A4:
|
|
|
|
case GPU_ETC1:
|
|
|
|
return 4;
|
2014-12-20 21:34:19 +01:00
|
|
|
default:
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-12 14:23:23 +01:00
|
|
|
static inline bool checkTexSize(u32 size)
|
|
|
|
{
|
|
|
|
if (size < 8 || size > 1024)
|
|
|
|
return false;
|
|
|
|
if (size & (size-1))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-02-12 00:14:04 +01:00
|
|
|
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)
|
2014-12-20 21:34:19 +01:00
|
|
|
{
|
2017-11-12 14:23:23 +01:00
|
|
|
if (!checkTexSize(p.width) || !checkTexSize(p.height)) return false;
|
2014-12-20 21:34:19 +01:00
|
|
|
|
2017-02-12 00:14:04 +01:00
|
|
|
bool isCube = typeIsCube(p.type);
|
|
|
|
if (isCube && !cube) return false;
|
|
|
|
|
2017-02-09 20:07:37 +01:00
|
|
|
u32 size = fmtSize(p.format);
|
2015-07-23 22:22:28 +02:00
|
|
|
if (!size) return false;
|
2017-02-09 20:07:37 +01:00
|
|
|
size *= (u32)p.width * p.height / 8;
|
2017-05-27 13:01:50 +02:00
|
|
|
u32 total_size = C3D_TexCalcTotalSize(size, p.maxLevel);
|
2014-12-20 21:34:19 +01:00
|
|
|
|
2017-02-12 00:14:04 +01:00
|
|
|
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;
|
|
|
|
}
|
2014-12-20 21:34:19 +01:00
|
|
|
|
2017-02-09 20:07:37 +01:00
|
|
|
tex->width = p.width;
|
|
|
|
tex->height = p.height;
|
2017-02-12 00:14:04 +01:00
|
|
|
tex->param = GPU_TEXTURE_MODE(p.type);
|
2017-02-09 20:07:37 +01:00
|
|
|
if (p.format == GPU_ETC1)
|
2016-05-03 08:41:05 +02:00
|
|
|
tex->param |= GPU_TEXTURE_ETC1_PARAM;
|
2017-02-09 20:07:37 +01:00
|
|
|
if (p.type == GPU_TEX_SHADOW_2D || p.type == GPU_TEX_SHADOW_CUBE)
|
|
|
|
tex->param |= GPU_TEXTURE_SHADOW_PARAM;
|
|
|
|
tex->fmt = p.format;
|
2015-07-23 22:22:28 +02:00
|
|
|
tex->size = size;
|
2017-02-09 20:07:37 +01:00
|
|
|
tex->border = 0;
|
|
|
|
tex->lodBias = 0;
|
|
|
|
tex->maxLevel = p.maxLevel;
|
|
|
|
tex->minLevel = 0;
|
2014-12-20 21:34:19 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-02-12 00:14:04 +01:00
|
|
|
void C3D_TexLoadImage(C3D_Tex* tex, const void* data, GPU_TEXFACE face, int level)
|
2015-11-28 15:51:07 +01:00
|
|
|
{
|
2017-02-09 20:07:37 +01:00
|
|
|
u32 size = 0;
|
2017-02-12 00:14:04 +01:00
|
|
|
void* out = C3D_TexGetImagePtr(tex,
|
|
|
|
C3Di_TexIs2D(tex) ? tex->data : tex->cube->data[face],
|
|
|
|
level, &size);
|
|
|
|
|
|
|
|
if (!addrIsVRAM(out))
|
|
|
|
memcpy(out, data, size);
|
|
|
|
else
|
2017-11-12 14:25:03 +01:00
|
|
|
C3D_SyncTextureCopy((u32*)data, 0, (u32*)out, 0, size, 8);
|
2015-11-28 15:51:07 +01:00
|
|
|
}
|
|
|
|
|
2017-02-09 20:07:37 +01:00
|
|
|
static void C3Di_DownscaleRGBA8(u32* dst, const u32* src[4])
|
2014-12-20 21:34:19 +01:00
|
|
|
{
|
2017-02-09 20:07:37 +01:00
|
|
|
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<<j;
|
|
|
|
}
|
|
|
|
*dst++ = dest;
|
|
|
|
}
|
2014-12-20 21:34:19 +01:00
|
|
|
}
|
|
|
|
|
2017-02-09 20:07:37 +01:00
|
|
|
static void C3Di_DownscaleRGB8(u8* dst, const u8* src[4])
|
2014-12-20 21:34:19 +01:00
|
|
|
{
|
2017-02-09 20:07:37 +01:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
2014-12-20 21:34:19 +01:00
|
|
|
}
|
|
|
|
|
2017-02-12 00:14:04 +01:00
|
|
|
void C3D_TexGenerateMipmap(C3D_Tex* tex, GPU_TEXFACE face)
|
2014-12-20 21:34:19 +01:00
|
|
|
{
|
2017-02-09 20:07:37 +01:00
|
|
|
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);
|
|
|
|
*/
|
|
|
|
|
2017-02-12 00:14:04 +01:00
|
|
|
void* src = C3Di_TexIs2D(tex) ? tex->data : tex->cube->data[face];
|
|
|
|
if (addrIsVRAM(src))
|
|
|
|
return; // CPU can't write to VRAM
|
|
|
|
|
2017-02-09 20:07:37 +01:00
|
|
|
int i;
|
|
|
|
u32 level_size = tex->size;
|
|
|
|
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
|
2017-11-12 14:25:03 +01:00
|
|
|
C3D_SyncDisplayTransfer(
|
2017-02-09 20:07:37 +01:00
|
|
|
(u32*)src, GX_BUFFER_DIM(src_width,src_height),
|
|
|
|
(u32*)dst, GX_BUFFER_DIM(dst_width,dst_height),
|
|
|
|
transfer_flags);
|
|
|
|
*/
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2014-12-20 21:34:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void C3D_TexBind(int unitId, C3D_Tex* tex)
|
|
|
|
{
|
|
|
|
C3D_Context* ctx = C3Di_GetContext();
|
|
|
|
|
|
|
|
if (!(ctx->flags & C3DiF_Active))
|
|
|
|
return;
|
|
|
|
|
2017-02-12 00:14:04 +01:00
|
|
|
if (unitId > 0 && C3D_TexGetType(tex) != GPU_TEX_2D)
|
2017-02-09 20:07:37 +01:00
|
|
|
return;
|
|
|
|
|
2014-12-20 21:34:19 +01:00
|
|
|
ctx->flags |= C3DiF_Tex(unitId);
|
|
|
|
ctx->tex[unitId] = tex;
|
|
|
|
}
|
|
|
|
|
|
|
|
void C3D_TexFlush(C3D_Tex* tex)
|
|
|
|
{
|
2017-02-15 17:42:23 +01:00
|
|
|
if (!addrIsVRAM(tex->data))
|
2017-02-12 00:14:04 +01:00
|
|
|
GSPGPU_FlushDataCache(tex->data, C3D_TexCalcTotalSize(tex->size, tex->maxLevel));
|
2014-12-20 21:34:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void C3D_TexDelete(C3D_Tex* tex)
|
|
|
|
{
|
2017-02-12 00:14:04 +01:00
|
|
|
if (C3Di_TexIs2D(tex))
|
|
|
|
allocFree(tex->data);
|
2015-11-28 15:51:07 +01:00
|
|
|
else
|
2017-02-12 00:14:04 +01:00
|
|
|
C3Di_TexCubeDelete(tex->cube);
|
2014-12-20 21:34:19 +01:00
|
|
|
}
|
2017-02-12 00:14:04 +01:00
|
|
|
|
2017-02-14 19:40:07 +01:00
|
|
|
void C3D_TexShadowParams(bool perspective, float bias)
|
|
|
|
{
|
|
|
|
C3D_Context* ctx = C3Di_GetContext();
|
|
|
|
|
|
|
|
if (!(ctx->flags & C3DiF_Active))
|
|
|
|
return;
|
|
|
|
|
|
|
|
u32 iBias = (u32)(fabs(bias) * BIT(24));
|
|
|
|
if (iBias >= BIT(24))
|
|
|
|
iBias = BIT(24)-1;
|
|
|
|
|
|
|
|
ctx->texShadow = (iBias &~ 1) | (perspective ? 0 : 1);
|
2018-03-13 13:14:32 +01:00
|
|
|
ctx->flags |= C3DiF_TexStatus;
|
2017-02-14 19:40:07 +01:00
|
|
|
}
|
|
|
|
|
2017-02-14 18:40:58 +01:00
|
|
|
void C3Di_SetTex(int unit, C3D_Tex* tex)
|
2017-02-12 00:14:04 +01:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2017-02-14 18:40:58 +01:00
|
|
|
case 0:
|
2017-02-12 00:14:04 +01:00
|
|
|
GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT0_BORDER_COLOR, reg, regcount);
|
|
|
|
GPUCMD_AddWrite(GPUREG_TEXUNIT0_TYPE, tex->fmt);
|
|
|
|
break;
|
2017-02-14 18:40:58 +01:00
|
|
|
case 1:
|
2017-02-12 00:14:04 +01:00
|
|
|
GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT1_BORDER_COLOR, reg, 5);
|
|
|
|
GPUCMD_AddWrite(GPUREG_TEXUNIT1_TYPE, tex->fmt);
|
|
|
|
break;
|
2017-02-14 18:40:58 +01:00
|
|
|
case 2:
|
2017-02-12 00:14:04 +01:00
|
|
|
GPUCMD_AddIncrementalWrites(GPUREG_TEXUNIT2_BORDER_COLOR, reg, 5);
|
|
|
|
GPUCMD_AddWrite(GPUREG_TEXUNIT2_TYPE, tex->fmt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|