mirror of
https://github.com/DaedalusX64/daedalus.git
synced 2025-04-02 10:21:48 -04:00
561 lines
13 KiB
C++
561 lines
13 KiB
C++
#include "BuildOptions.h"
|
|
#include "Base/Types.h"
|
|
|
|
#ifdef DAEDALUS_ACCURATE_TMEM
|
|
#include "Core/ROM.h"
|
|
#include "HLEGraphics/ConvertFormats.h"
|
|
#include "HLEGraphics/ConvertTile.h"
|
|
#include "HLEGraphics/RDP.h"
|
|
#include "HLEGraphics/TextureInfo.h"
|
|
#include "Graphics/NativePixelFormat.h"
|
|
#include "System/Endian.h"
|
|
|
|
#include <vector>
|
|
|
|
struct TileDestInfo
|
|
{
|
|
explicit TileDestInfo( ETextureFormat tex_fmt )
|
|
: Format( tex_fmt )
|
|
, Width( 0 )
|
|
, Height( 0 )
|
|
, Pitch( 0 )
|
|
, Data( nullptr )
|
|
//, Palette( nullptr )
|
|
{
|
|
}
|
|
|
|
ETextureFormat Format;
|
|
u32 Width; // Describes the width of the locked area. Use lPitch to move between successive lines
|
|
u32 Height; // Describes the height of the locked area
|
|
s32 Pitch; // Specifies the number of bytes on each row (not necessarily bitdepth*width/8)
|
|
void * Data; // Pointer to the top left pixel of the image
|
|
//NativePf8888 * Palette;
|
|
};
|
|
|
|
extern u8 gTMEM[4096];
|
|
|
|
|
|
static void ConvertRGBA32(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u32 * dst = static_cast<u32*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch / sizeof(u32);
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
// NB! RGBA/32 line needs to be doubled.
|
|
src_row_stride *= 2;
|
|
|
|
u32 row_swizzle = 0;
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
for (u32 x = 0; x < width; ++x)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 r = src[(o+3)&0xfff];
|
|
u8 b = src[(o+1)&0xfff];
|
|
u8 g = src[(o+2)&0xfff];
|
|
u8 a = src[(o+0)&0xfff];
|
|
|
|
dst[dst_offset+0] = RGBA32(r, g, b, a);
|
|
|
|
src_offset += 4;
|
|
dst_offset += 1;
|
|
}
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x8; // Alternate lines are qword-swapped
|
|
}
|
|
}
|
|
|
|
static void ConvertRGBA16(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u32 * dst = static_cast<u32*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch / sizeof(u32);
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
u32 row_swizzle = 0;
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
for (u32 x = 0; x < width; ++x)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel_hi = src[(o+0)&0xfff];
|
|
u8 src_pixel_lo = src[(o+1)&0xfff];
|
|
u16 src_pixel = (src_pixel_hi << 8) | src_pixel_lo;
|
|
|
|
dst[dst_offset+0] = RGBA16(src_pixel);
|
|
|
|
src_offset += 2;
|
|
dst_offset += 1;
|
|
}
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x4; // Alternate lines are word-swapped
|
|
}
|
|
}
|
|
|
|
template <u32 (*PalConvertFn)(u16)>
|
|
static void ConvertCI8T(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u32 * dst = static_cast<u32*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch / sizeof(u32);
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
const u16 * src16 = (u16*)src;
|
|
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
// Convert the palette once, here.
|
|
u32 palette[256];
|
|
for (u32 i = 0; i < 256; ++i)
|
|
{
|
|
u16 src_pixel = src16[0x400+(i<<2)];
|
|
palette[i] = PalConvertFn(src_pixel);
|
|
}
|
|
|
|
u32 row_swizzle = 0;
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
for (u32 x = 0; x < width; ++x)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel = src[o&0xfff];
|
|
|
|
dst[dst_offset+0] = palette[src_pixel];
|
|
|
|
src_offset += 1;
|
|
dst_offset += 1;
|
|
}
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x4; // Alternate lines are word-swapped
|
|
}
|
|
}
|
|
|
|
template <u32 (*PalConvertFn)(u16)>
|
|
static void ConvertCI4T(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u32 * dst = static_cast<u32*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch / sizeof(u32);
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
const u16 * src16 = (u16*)src;
|
|
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
// Convert the palette once, here.
|
|
u32 pal_address = 0x400 + (ti.GetPalette()<<6);
|
|
u32 palette[16];
|
|
for (u32 i = 0; i < 16; ++i)
|
|
{
|
|
u16 src_pixel = src16[pal_address+(i<<2)];
|
|
palette[i] = PalConvertFn(src_pixel);
|
|
}
|
|
|
|
u32 row_swizzle = 0;
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
|
|
// Process 2 pixels at a time
|
|
for (u32 x = 0; x+1 < width; x += 2)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel = src[o&0xfff];
|
|
|
|
dst[dst_offset+0] = palette[(src_pixel&0xf0)>>4];
|
|
dst[dst_offset+1] = palette[(src_pixel&0x0f)>>0];
|
|
|
|
src_offset += 1;
|
|
dst_offset += 2;
|
|
}
|
|
|
|
// Handle trailing pixel, if odd width
|
|
if (width&1)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel = src[o&0xfff];
|
|
|
|
dst[dst_offset+0] = palette[(src_pixel&0xf0)>>4];
|
|
|
|
src_offset += 1;
|
|
dst_offset += 1;
|
|
}
|
|
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x4; // Alternate lines are word-swapped
|
|
}
|
|
}
|
|
|
|
static void ConvertCI8(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
switch (ti.GetTLutFormat())
|
|
{
|
|
case kTT_RGBA16:
|
|
ConvertCI8T< RGBA16 >(dsti, ti);
|
|
break;
|
|
case kTT_IA16:
|
|
ConvertCI8T< IA16 >(dsti, ti);
|
|
break;
|
|
default:
|
|
DAEDALUS_ERROR("Unhandled tlut format %d/%d", ti.GetTLutFormat());
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void ConvertCI4(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
switch (ti.GetTLutFormat())
|
|
{
|
|
case kTT_RGBA16:
|
|
ConvertCI4T< RGBA16 >(dsti, ti);
|
|
break;
|
|
case kTT_IA16:
|
|
ConvertCI4T< IA16 >(dsti, ti);
|
|
break;
|
|
default:
|
|
DAEDALUS_ERROR("Unhandled tlut format %d/%d", ti.GetTLutFormat());
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void ConvertIA16(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u32 * dst = static_cast<u32*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch / sizeof(u32);
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
u32 row_swizzle = 0;
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
for (u32 x = 0; x < width; ++x)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel_hi = src[(o+0)&0xfff];
|
|
u8 src_pixel_lo = src[(o+1)&0xfff];
|
|
u16 src_pixel = (src_pixel_hi << 8) | src_pixel_lo;
|
|
|
|
dst[dst_offset+0] = IA16(src_pixel);
|
|
|
|
src_offset += 2;
|
|
dst_offset += 1;
|
|
}
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x4; // Alternate lines are word-swapped
|
|
}
|
|
}
|
|
|
|
static void ConvertIA8(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u8 * dst = static_cast<u8*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch;
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
u32 row_swizzle = 0;
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
for (u32 x = 0; x < width; ++x)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel = src[o&0xfff];
|
|
|
|
u8 i = FourToEight[(src_pixel>>4)&0xf];
|
|
u8 a = FourToEight[(src_pixel )&0xf];
|
|
|
|
dst[dst_offset+0] = i;
|
|
dst[dst_offset+1] = i;
|
|
dst[dst_offset+2] = i;
|
|
dst[dst_offset+3] = a;
|
|
|
|
src_offset += 1;
|
|
dst_offset += 4;
|
|
}
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x4; // Alternate lines are word-swapped
|
|
}
|
|
}
|
|
|
|
static void ConvertIA4(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u32 * dst = static_cast<u32*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch / sizeof(u32);
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
u32 row_swizzle = 0;
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
|
|
// Process 2 pixels at a time
|
|
for (u32 x = 0; x+1 < width; x += 2)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel = src[o&0xfff];
|
|
|
|
dst[dst_offset+0] = IA4(src_pixel>>4);
|
|
dst[dst_offset+1] = IA4((src_pixel&0xf));
|
|
|
|
src_offset += 1;
|
|
dst_offset += 2;
|
|
}
|
|
|
|
// Handle trailing pixel, if odd width
|
|
if (width&1)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel = src[o&0xfff];
|
|
|
|
dst[dst_offset+0] = IA4(src_pixel>>4);
|
|
|
|
src_offset += 1;
|
|
dst_offset += 1;
|
|
}
|
|
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x4; // Alternate lines are word-swapped
|
|
}
|
|
}
|
|
|
|
static void ConvertI8(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u8 * dst = static_cast<u8*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch;
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
u32 row_swizzle = 0;
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
for (u32 x = 0; x < width; ++x)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 i = src[o&0xfff];
|
|
|
|
dst[dst_offset+0] = i;
|
|
dst[dst_offset+1] = i;
|
|
dst[dst_offset+2] = i;
|
|
dst[dst_offset+3] = i;
|
|
|
|
src_offset += 1;
|
|
dst_offset += 4;
|
|
}
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x4; // Alternate lines are word-swapped
|
|
}
|
|
}
|
|
|
|
static void ConvertI4(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u32 * dst = static_cast<u32*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch / sizeof(u32);
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
u32 row_swizzle = 0;
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
|
|
// Process 2 pixels at a time
|
|
for (u32 x = 0; x+1 < width; x += 2)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel = src[o&0xfff];
|
|
|
|
dst[dst_offset+0] = I4(src_pixel>>4);
|
|
dst[dst_offset+1] = I4((src_pixel&0xf));
|
|
|
|
src_offset += 1;
|
|
dst_offset += 2;
|
|
}
|
|
|
|
// Handle trailing pixel, if odd width
|
|
if (width&1)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
u8 src_pixel = src[o&0xfff];
|
|
|
|
dst[dst_offset+0] = I4(src_pixel>>4);
|
|
|
|
src_offset += 1;
|
|
dst_offset += 1;
|
|
}
|
|
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x4; // Alternate lines are word-swapped
|
|
}
|
|
}
|
|
|
|
static void ConvertYUV16(const TileDestInfo & dsti, const TextureInfo & ti)
|
|
{
|
|
u32 width = dsti.Width;
|
|
u32 height = dsti.Height;
|
|
|
|
u32 * dst = static_cast<u32*>(dsti.Data);
|
|
u32 dst_row_stride = dsti.Pitch / sizeof(u32);
|
|
u32 dst_row_offset = 0;
|
|
|
|
const u8 * src = gTMEM;
|
|
u32 src_row_stride = ti.GetLine()<<3;
|
|
u32 src_row_offset = ti.GetTmemAddress()<<3;
|
|
|
|
// NB! YUV/16 line needs to be doubled.
|
|
src_row_stride *= 2;
|
|
u32 row_swizzle = 0;
|
|
|
|
for (u32 y = 0; y < height; ++y)
|
|
{
|
|
u32 src_offset = src_row_offset;
|
|
u32 dst_offset = dst_row_offset;
|
|
|
|
// Process 2 pixels at a time
|
|
for (u32 x = 0; x < width; x += 2)
|
|
{
|
|
u32 o = src_offset^row_swizzle;
|
|
s32 y0 = src[(o+1)&0xfff];
|
|
s32 y1 = src[(o+3)&0xfff];
|
|
s32 u0 = src[(o+0)&0xfff];
|
|
s32 v0 = src[(o+2)&0xfff];
|
|
|
|
dst[dst_offset+0] = YUV16(y0,u0,v0);
|
|
dst[dst_offset+1] = YUV16(y1,u0,v0);
|
|
|
|
src_offset += 4;
|
|
dst_offset += 2;
|
|
}
|
|
src_row_offset += src_row_stride;
|
|
dst_row_offset += dst_row_stride;
|
|
|
|
row_swizzle ^= 0x4; // Alternate lines are word-swapped
|
|
}
|
|
}
|
|
using ConvertFunction = void (*)(const TileDestInfo & dsti, const TextureInfo & ti);
|
|
|
|
static const ConvertFunction gConvertFunctions[ 32 ] =
|
|
{
|
|
// 4bpp 8bpp 16bpp 32bpp
|
|
nullptr, nullptr, ConvertRGBA16, ConvertRGBA32, // RGBA
|
|
nullptr, nullptr, ConvertYUV16, nullptr, // YUV
|
|
ConvertCI4, ConvertCI8, nullptr, nullptr, // CI
|
|
ConvertIA4, ConvertIA8, ConvertIA16, nullptr, // IA
|
|
ConvertI4, ConvertI8, nullptr, nullptr, // I
|
|
nullptr, nullptr, nullptr, nullptr, // ?
|
|
nullptr, nullptr, nullptr, nullptr, // ?
|
|
nullptr, nullptr, nullptr, nullptr // ?
|
|
};
|
|
|
|
bool ConvertTile(const TextureInfo & ti,
|
|
void * texels,
|
|
NativePf8888 * palette,
|
|
ETextureFormat texture_format,
|
|
u32 pitch)
|
|
{
|
|
DAEDALUS_ASSERT(texture_format == TexFmt_8888, "OSX should only use RGBA 8888 textures");
|
|
|
|
TileDestInfo dsti( texture_format );
|
|
dsti.Data = texels;
|
|
dsti.Width = ti.GetWidth();
|
|
dsti.Height = ti.GetHeight();
|
|
dsti.Pitch = pitch;
|
|
//dsti.Palette = palette;
|
|
|
|
DAEDALUS_ASSERT(ti.GetLine() != 0, "No line");
|
|
|
|
const ConvertFunction fn = gConvertFunctions[ (ti.GetFormat() << 2) | ti.GetSize() ];
|
|
if( fn )
|
|
{
|
|
fn( dsti, ti );
|
|
return true;
|
|
}
|
|
|
|
DAEDALUS_ERROR("Unhandled format %d/%d", ti.GetFormat(), ti.GetSize());
|
|
return false;
|
|
}
|
|
#endif //DAEDALUS_ACCURATE_TMEM
|