#include #include #include #include // std::this_thread::sleep_for #include // std::chrono::seconds #include "Platform.h" #include "Textures.h" #include "GBI.h" #include "RSP.h" #include "RDP.h" #include "gDP.h" #include "gSP.h" #include "N64.h" #include "convert.h" #include "FrameBuffer.h" #include "Config.h" #include "GLideNHQ/TxFilterExport.h" #include "TextureFilterHandler.h" #include "DisplayLoadProgress.h" #include "Graphics/Context.h" #include "Graphics/Parameters.h" #include "DisplayWindow.h" #ifdef NATIVE #define RDRAM ((u8*)0) #endif using namespace std; using namespace graphics; u32 GetNone(u16 offset, u16 x, u16 i, u8 palette) { return 0x00000000; } inline u8 Get4BitPaletteColor(u16 offset, u16 x, u16 i) { u8* tmem8 = reinterpret_cast(TMEM); return tmem8[((offset << 3) + ((x >> 1) ^ (i << 1))) & 0xFFF]; } u32 GetCI4_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); return CI4_RGBA8888((x & 1) ? (palette << 4) | (color4B & 0x0F) : (palette << 4) | (color4B >> 4)); } u32 GetCI4_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); return CI4_RGBA4444((x & 1) ? (palette << 4) | (color4B & 0x0F) : (palette << 4) | (color4B >> 4)); } u32 GetCI4IA_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); if (x & 1) return IA88_RGBA4444(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF]); else return IA88_RGBA4444(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF]); } u32 GetCI4IA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); if (x & 1) return IA88_RGBA8888(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF]); else return IA88_RGBA8888(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF]); } u32 GetCI4RGBA_RGBA5551(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); if (x & 1) return RGBA5551_RGBA5551(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF]); else return RGBA5551_RGBA5551(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF]); } u32 GetCI4RGBA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); if (x & 1) return RGBA5551_RGBA8888(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B & 0x0F)) & 0x1FF]); else return RGBA5551_RGBA8888(*(u16*)&TMEM[(0x100 + (palette << 4) + (color4B >> 4)) & 0x1FF]); } u32 GetIA31_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); return IA31_RGBA8888((x & 1) ? (color4B & 0x0F) : (color4B >> 4)); } u32 GetIA31_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); return IA31_RGBA4444((x & 1) ? (color4B & 0x0F) : (color4B >> 4)); } u32 GetI4_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); return I4_RGBA8888((x & 1) ? (color4B & 0x0F) : (color4B >> 4)); } u32 GetI4_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u8 color4B = Get4BitPaletteColor(offset, x, i); return I4_RGBA4444((x & 1) ? (color4B & 0x0F) : (color4B >> 4)); } inline u8 Get8BitPaletteColor(u16 offset, u16 x, u16 i) { u8* tmem8 = reinterpret_cast(TMEM); return tmem8[((offset << 3) + (x ^ (i << 1))) & 0xFFF]; } u32 GetCI8IA_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); return IA88_RGBA4444(*(u16*)&TMEM[(0x100 + color) & 0x1FF]); } u32 GetCI8IA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); return IA88_RGBA8888(*(u16*)&TMEM[(0x100 + color) & 0x1FF]); } u32 GetCI8RGBA_RGBA5551(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); return RGBA5551_RGBA5551(*(u16*)&TMEM[(0x100 + color) & 0x1FF]); } u32 GetCI8RGBA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); return RGBA5551_RGBA8888(*(u16*)&TMEM[(0x100 + color) & 0x1FF]); } u32 GetIA44_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); return IA44_RGBA8888(color); } u32 GetIA44_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); return IA44_RGBA4444(color); } u32 GetI8_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); return I8_RGBA8888(color); } u32 GetI8_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u8 color = Get8BitPaletteColor(offset, x, i); return I8_RGBA4444(color); } inline u16 Get16BitColor(u16 offset, u16 x, u16 i) { u16* tmem16 = reinterpret_cast(TMEM); return tmem16[((offset << 2) + (x ^ i)) & 0x7FF]; } u32 GetI16_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); u32 r = tex >> 8; u32 g = tex & 0xFF; u32 b = r; u32 a = g; return (a << 24) | (b << 16) | (g << 8) | r; } u32 GetI16_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); u16 r = tex >> 12; u16 g = tex & 0x0F; u16 b = r; u16 a = g; return (a << 12) | (b << 8) | (g << 4) | r; } u32 GetCI16IA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); const u16 col = (*(u16*)&TMEM[0x100 + (tex & 0xFF)]); const u16 c = col >> 8; const u16 a = col & 0xFF; return (a << 24) | (c << 16) | (c << 8) | c; } u32 GetCI16IA_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); const u16 col = (*(u16*)&TMEM[0x100 + (tex & 0xFF)]); const u16 c = col >> 12; const u16 a = col & 0x0F; return (a << 12) | (c << 8) | (c << 4) | c; } u32 GetCI16RGBA_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i) & 0xFF; return RGBA5551_RGBA8888(((u16*)&TMEM[0x100])[tex << 2]); } u32 GetCI16RGBA_RGBA5551(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i) & 0xFF; return RGBA5551_RGBA5551(((u16*)&TMEM[0x100])[tex << 2]); } u32 GetRGBA5551_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); return RGBA5551_RGBA8888(tex); } u32 GetRGBA5551_RGBA5551(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); return RGBA5551_RGBA5551(tex); } u32 GetIA88_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); return IA88_RGBA8888(tex); } u32 GetIA88_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u16 tex = Get16BitColor(offset, x, i); return IA88_RGBA4444(tex); } inline u32 Get32BitColor(u16 offset, u16 x, u16 i) { u32* tmem32 = reinterpret_cast(TMEM); return tmem32[((offset << 1) + (x ^ i)) & LOAD_BLOCK32_MASK]; } u32 GetRGBA8888_RGBA8888(u16 offset, u16 x, u16 i, u8 palette) { return Get32BitColor(offset, x, i); } u32 GetRGBA8888_RGBA4444(u16 offset, u16 x, u16 i, u8 palette) { const u32 tex = Get32BitColor(offset, x, i); return RGBA8888_RGBA4444(tex); } inline u32 YUV_RGBA8888(u8 y, u8 u, u8 v) { return (0xff << 24) | (y << 16) | (v << 8) | u; } void GetYUV_RGBA8888(u64 * src, u32 * dst, u16 x) { const u32 t = (((u32*)src)[x]); u8 y1 = (u8)t & 0xFF; u8 v = (u8)(t >> 8) & 0xFF; u8 y0 = (u8)(t >> 16) & 0xFF; u8 u = (u8)(t >> 24) & 0xFF; u32 c = YUV_RGBA8888(y0, u, v); *(dst++) = c; c = YUV_RGBA8888(y1, u, v); *(dst++) = c; } u32 GetNoneBG(u64 *src, u16 x, u16 i, u8 palette) { return 0x00000000; } u32 GetCI4_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; return CI4_RGBA8888((x & 1) ? (palette << 4) | (color4B & 0x0F) : (palette << 4) | (color4B >> 4)); } u32 GetCI4_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; return CI4_RGBA4444((x & 1) ? (palette << 4) | (color4B & 0x0F) : (palette << 4) | (color4B >> 4)); } u32 GetCI4IA_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; if (x & 1) return IA88_RGBA4444(*(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)]); else return IA88_RGBA4444(*(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)]); } u32 GetCI4IA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; if (x & 1) return IA88_RGBA8888(*(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)]); else return IA88_RGBA8888(*(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)]); } u32 GetCI4RGBA_RGBA5551_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; if (x & 1) return RGBA5551_RGBA5551(*(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)]); else return RGBA5551_RGBA5551(*(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)]); } u32 GetCI4RGBA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; if (x & 1) return RGBA5551_RGBA8888(*(u16*)&TMEM[256 + (palette << 4) + (color4B & 0x0F)]); else return RGBA5551_RGBA8888(*(u16*)&TMEM[256 + (palette << 4) + (color4B >> 4)]); } u32 GetIA31_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; return IA31_RGBA8888((x & 1) ? (color4B & 0x0F) : (color4B >> 4)); } u32 GetIA31_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; return IA31_RGBA4444((x & 1) ? (color4B & 0x0F) : (color4B >> 4)); } u32 GetI4_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; return I4_RGBA8888((x & 1) ? (color4B & 0x0F) : (color4B >> 4)); } u32 GetI4_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { u8 color4B = ((u8*)src)[(x >> 1) ^ (i << 1)]; return I4_RGBA4444((x & 1) ? (color4B & 0x0F) : (color4B >> 4)); } u32 GetCI8IA_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { return IA88_RGBA4444(*(u16*)&TMEM[256 + ((u8*)src)[x ^ (i << 1)]]); } u32 GetCI8IA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { return IA88_RGBA8888(*(u16*)&TMEM[256 + ((u8*)src)[x ^ (i << 1)]]); } u32 GetCI8RGBA_RGBA5551_BG(u64 *src, u16 x, u16 i, u8 palette) { return RGBA5551_RGBA5551(*(u16*)&TMEM[256 + ((u8*)src)[x ^ (i << 1)]]); } u32 GetCI8RGBA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { return RGBA5551_RGBA8888(*(u16*)&TMEM[256 + ((u8*)src)[x ^ (i << 1)]]); } u32 GetIA44_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { return IA44_RGBA8888(((u8*)src)[x ^ (i << 1)]); } u32 GetIA44_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { return IA44_RGBA4444(((u8*)src)[x ^ (i << 1)]); } u32 GetI8_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { return I8_RGBA8888(((u8*)src)[x ^ (i << 1)]); } u32 GetI8_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { return I8_RGBA4444(((u8*)src)[x ^ (i << 1)]); } u32 GetI16_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { const u16 tex = ((u16*)src)[x^i]; u32 r = tex >> 8; u32 g = tex & 0xFF; u32 b = r; u32 a = g; return (a << 24) | (b << 16) | (g << 8) | r; } u32 GetI16_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { const u16 tex = ((u16*)src)[x^i]; u16 r = tex >> 12; u16 g = tex & 0x0F; u16 b = r; u16 a = g; return (a << 12) | (b << 8) | (g << 4) | r; } u32 GetCI16IA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { const u16 tex = ((u16*)src)[x^i]; const u16 col = (*(u16*)&TMEM[256 + (tex & 0xFF)]); const u16 c = col >> 8; const u16 a = col & 0xFF; return (a << 24) | (c << 16) | (c << 8) | c; } u32 GetCI16IA_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { const u16 tex = ((u16*)src)[x^i]; const u16 col = (*(u16*)&TMEM[256 + (tex & 0xFF)]); const u16 c = col >> 12; const u16 a = col & 0x0F; return (a << 12) | (c << 8) | (c << 4) | c; } u32 GetCI16RGBA_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { const u16 tex = (((u16*)src)[x^i]) & 0xFF; return RGBA5551_RGBA8888(((u16*)&TMEM[256])[tex << 2]); } u32 GetCI16RGBA_RGBA5551_BG(u64 *src, u16 x, u16 i, u8 palette) { const u16 tex = (((u16*)src)[x^i]) & 0xFF; return RGBA5551_RGBA5551(((u16*)&TMEM[256])[tex << 2]); } u32 GetRGBA5551_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { u16 tex = ((u16*)src)[x^i]; return RGBA5551_RGBA8888(tex); } u32 GetRGBA5551_RGBA5551_BG(u64 *src, u16 x, u16 i, u8 palette) { u16 tex = ((u16*)src)[x^i]; return RGBA5551_RGBA5551(tex); } u32 GetIA88_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { return IA88_RGBA8888(((u16*)src)[x^i]); } u32 GetIA88_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { return IA88_RGBA4444(((u16*)src)[x^i]); } u32 GetRGBA8888_RGBA8888_BG(u64 *src, u16 x, u16 i, u8 palette) { return ((u32*)src)[x^i]; } u32 GetRGBA8888_RGBA4444_BG(u64 *src, u16 x, u16 i, u8 palette) { return RGBA8888_RGBA4444(((u32*)src)[x^i]); } struct TextureLoadParameters { GetTexelFunc Get16; GetTexelFuncBG Get16BG; DatatypeParam glType16; InternalColorFormatParam glInternalFormat16; GetTexelFunc Get32; GetTexelFuncBG Get32BG; DatatypeParam glType32; InternalColorFormatParam glInternalFormat32; InternalColorFormatParam autoFormat; u32 lineShift; u32 maxTexels; }; struct ImageFormat { ImageFormat(); TextureLoadParameters tlp[4][4][5]; static ImageFormat & get() { static ImageFormat imageFmt; return imageFmt; } }; ImageFormat::ImageFormat() { TextureLoadParameters imageFormat[4][4][5] = { // G_TT_NONE { // Get16 glType16 glInternalFormat16 Get32 glType32 glInternalFormat32 autoFormat { // 4-bit { GetI4_RGBA4444, GetI4_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetI4_RGBA8888, GetI4_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 4, 8192 }, // RGBA as I { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 4, 8192 }, // YUV { GetCI4_RGBA4444, GetCI4_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI4_RGBA8888, GetCI4_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 4, 8192 }, // CI without palette { GetIA31_RGBA4444, GetIA31_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetIA31_RGBA8888, GetIA31_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 4, 8192 }, // IA { GetI4_RGBA4444, GetI4_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetI4_RGBA8888, GetI4_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 4, 8192 }, // I }, { // 8-bit { GetI8_RGBA4444, GetI8_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetI8_RGBA8888, GetI8_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 3, 4096 }, // RGBA as I { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 4096 }, // YUV { GetI8_RGBA4444, GetI8_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetI8_RGBA8888, GetI8_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 3, 4096 }, // CI without palette { GetIA44_RGBA4444, GetIA44_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetIA44_RGBA8888, GetIA44_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 3, 4096 }, // IA { GetI8_RGBA4444, GetI8_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetI8_RGBA8888, GetI8_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 3, 4096 }, // I }, { // 16-bit { GetRGBA5551_RGBA5551, GetRGBA5551_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetRGBA5551_RGBA8888, GetRGBA5551_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 2, 2048 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 2048 }, // YUV { GetIA88_RGBA4444, GetIA88_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetIA88_RGBA8888, GetIA88_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 2048 }, // CI as IA { GetIA88_RGBA4444, GetIA88_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetIA88_RGBA8888, GetIA88_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 2048 }, // IA { GetI16_RGBA4444, GetI16_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetI16_RGBA8888, GetI16_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 0, 2048 }, // I }, { // 32-bit { GetRGBA8888_RGBA4444, GetRGBA8888_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetRGBA8888_RGBA8888, GetRGBA8888_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 1024 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // YUV { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // CI { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // IA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // I } }, // DUMMY { // Get16 glType16 glInternalFormat16 Get32 glType32 glInternalFormat32 autoFormat { // 4-bit { GetCI4RGBA_RGBA5551, GetCI4RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI4RGBA_RGBA8888, GetCI4RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 4, 4096 }, // CI (Banjo-Kazooie uses this, doesn't make sense, but it works...) { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 4, 8192 }, // YUV { GetCI4RGBA_RGBA5551, GetCI4RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI4RGBA_RGBA8888, GetCI4RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 4, 4096 }, // CI { GetCI4RGBA_RGBA5551, GetCI4RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI4RGBA_RGBA8888, GetCI4RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 4, 4096 }, // IA as CI { GetCI4RGBA_RGBA5551, GetCI4RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI4RGBA_RGBA8888, GetCI4RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 4, 4096 }, // I as CI }, { // 8-bit { GetCI8RGBA_RGBA5551, GetCI8RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI8RGBA_RGBA8888, GetCI8RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 3, 2048 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 4096 }, // YUV { GetCI8RGBA_RGBA5551, GetCI8RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI8RGBA_RGBA8888, GetCI8RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 3, 2048 }, // CI { GetCI8RGBA_RGBA5551, GetCI8RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI8RGBA_RGBA8888, GetCI8RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 3, 2048 }, // IA as CI { GetCI8RGBA_RGBA5551, GetCI8RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI8RGBA_RGBA8888, GetCI8RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 3, 2048 }, // I as CI }, { // 16-bit { GetCI16RGBA_RGBA5551, GetCI16RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetRGBA5551_RGBA8888, GetRGBA5551_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 2, 2048 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 2048 }, // YUV { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 2048 }, // CI { GetCI16RGBA_RGBA5551, GetCI16RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI16RGBA_RGBA8888, GetCI16RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 2, 2048 }, // IA as CI { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 2048 }, // I }, { // 32-bit { GetRGBA8888_RGBA4444, GetRGBA8888_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetRGBA8888_RGBA8888, GetRGBA8888_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 1024 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // YUV { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // CI { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // IA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // I } }, // G_TT_RGBA16 { // Get16 glType16 glInternalFormat16 Get32 glType32 glInternalFormat32 autoFormat { // 4-bit { GetCI4RGBA_RGBA5551, GetCI4RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI4RGBA_RGBA8888, GetCI4RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 4, 4096 }, // CI (Banjo-Kazooie uses this, doesn't make sense, but it works...) { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 4, 8192 }, // YUV { GetCI4RGBA_RGBA5551, GetCI4RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI4RGBA_RGBA8888, GetCI4RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 4, 4096 }, // CI { GetCI4RGBA_RGBA5551, GetCI4RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI4RGBA_RGBA8888, GetCI4RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 4, 4096 }, // IA as CI { GetCI4RGBA_RGBA5551, GetCI4RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI4RGBA_RGBA8888, GetCI4RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 4, 4096 }, // I as CI }, { // 8-bit { GetCI8RGBA_RGBA5551, GetCI8RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI8RGBA_RGBA8888, GetCI8RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 3, 2048 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 4096 }, // YUV { GetCI8RGBA_RGBA5551, GetCI8RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI8RGBA_RGBA8888, GetCI8RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 3, 2048 }, // CI { GetCI8RGBA_RGBA5551, GetCI8RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI8RGBA_RGBA8888, GetCI8RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 3, 2048 }, // IA as CI { GetCI8RGBA_RGBA5551, GetCI8RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI8RGBA_RGBA8888, GetCI8RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 3, 2048 }, // I as CI }, { // 16-bit { GetCI16RGBA_RGBA5551, GetCI16RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetRGBA5551_RGBA8888, GetRGBA5551_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 2, 2048 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 2048 }, // YUV { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 2048 }, // CI { GetCI16RGBA_RGBA5551, GetCI16RGBA_RGBA5551_BG, datatype::UNSIGNED_SHORT_5_5_5_1, internalcolorFormat::RGB5_A1, GetCI16RGBA_RGBA8888, GetCI16RGBA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGB5_A1, 2, 2048 }, // IA as CI { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 2048 }, // I }, { // 32-bit { GetRGBA8888_RGBA4444, GetRGBA8888_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetRGBA8888_RGBA8888, GetRGBA8888_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 1024 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // YUV { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // CI { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // IA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA4, 0, 1024 }, // I } }, // G_TT_IA16 { // Get16 glType16 glInternalFormat16 Get32 glType32 glInternalFormat32 autoFormat { // 4-bit { GetCI4IA_RGBA4444, GetCI4IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI4IA_RGBA8888, GetCI4IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 4, 4096 }, // IA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 4, 8192 }, // YUV { GetCI4IA_RGBA4444, GetCI4IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI4IA_RGBA8888, GetCI4IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 4, 4096 }, // CI { GetCI4IA_RGBA4444, GetCI4IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI4IA_RGBA8888, GetCI4IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 4, 4096 }, // IA { GetCI4IA_RGBA4444, GetCI4IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI4IA_RGBA8888, GetCI4IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 4, 4096 }, // I }, { // 8-bit { GetCI8IA_RGBA4444, GetCI8IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI8IA_RGBA8888, GetCI8IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 3, 2048 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 0, 4096 }, // YUV { GetCI8IA_RGBA4444, GetCI8IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI8IA_RGBA8888, GetCI8IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 3, 2048 }, // CI { GetCI8IA_RGBA4444, GetCI8IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI8IA_RGBA8888, GetCI8IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 3, 2048 }, // IA { GetCI8IA_RGBA4444, GetCI8IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI8IA_RGBA8888, GetCI8IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 3, 2048 }, // I }, { // 16-bit { GetCI16IA_RGBA4444, GetCI16IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI16IA_RGBA8888, GetCI16IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 2048 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 2048 }, // YUV { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 0, 2048 }, // CI { GetCI16IA_RGBA4444, GetCI16IA_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetCI16IA_RGBA8888, GetCI16IA_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 2048 }, // IA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 0, 2048 }, // I }, { // 32-bit { GetRGBA8888_RGBA4444, GetRGBA8888_RGBA4444_BG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetRGBA8888_RGBA8888, GetRGBA8888_RGBA8888_BG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 2, 1024 }, // RGBA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 0, 1024 }, // YUV { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 0, 1024 }, // CI { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 0, 1024 }, // IA { GetNone, GetNoneBG, datatype::UNSIGNED_SHORT_4_4_4_4, internalcolorFormat::RGBA4, GetNone, GetNoneBG, datatype::UNSIGNED_BYTE, internalcolorFormat::RGBA8, internalcolorFormat::RGBA8, 0, 1024 }, // I } } }; memcpy(tlp, imageFormat, sizeof(tlp)); } /** cite from RiceVideo */ inline u32 CalculateDXT(u32 txl2words) { if (txl2words == 0) return 1; else return (2048 + txl2words - 1) / txl2words; } u32 sizeBytes[4] = {0, 1, 2, 4}; inline u32 Txl2Words(u32 width, u32 size) { if (size == 0) return max(1U, width / 16); else return max(1U, width*sizeBytes[size] / 8); } inline u32 ReverseDXT(u32 val, u32 lrs, u32 width, u32 size) { if (val == 0x800) return 1; int low = 2047 / val; if (CalculateDXT(low) > val) low++; int high = 2047 / (val - 1); if (low == high) return low; for (int i = low; i <= high; i++) { if (Txl2Words(width, size) == (u32)i) return i; } return (low + high) / 2; } /** end RiceVideo cite */ TextureCache & TextureCache::get() { static TextureCache cache; return cache; } void TextureCache::_initDummyTexture(CachedTexture * _pDummy) { _pDummy->address = 0; _pDummy->clampS = 1; _pDummy->clampT = 1; _pDummy->clampWidth = 2; _pDummy->clampHeight = 2; _pDummy->crc = 0; _pDummy->format = 0; _pDummy->size = 0; _pDummy->frameBufferTexture = CachedTexture::fbNone; _pDummy->width = 2; _pDummy->height = 2; _pDummy->maskS = 0; _pDummy->maskT = 0; _pDummy->scaleS = 0.5f; _pDummy->scaleT = 0.5f; _pDummy->hdRatioS = 1.0f; _pDummy->hdRatioT = 1.0f; _pDummy->shiftScaleS = 1.0f; _pDummy->shiftScaleT = 1.0f; _pDummy->textureBytes = 2 * 2 * 4; _pDummy->tMem = 0; } void TextureCache::init() { m_curUnpackAlignment = 0; u32 dummyTexture[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; m_pDummy = addFrameBufferTexture(textureTarget::TEXTURE_2D); // we don't want to remove dummy texture _initDummyTexture(m_pDummy); Context::InitTextureParams params; params.handle = m_pDummy->name; params.mipMapLevel = 0; params.msaaLevel = 0; params.width = m_pDummy->width; params.height = m_pDummy->height; params.format = colorFormat::RGBA; params.internalFormat = gfxContext.convertInternalTextureFormat(u32(internalcolorFormat::RGBA8)); params.dataType = datatype::UNSIGNED_BYTE; params.data = dummyTexture; gfxContext.init2DTexture(params); activateDummy( 0 ); activateDummy(1); current[0] = current[1] = nullptr; m_pMSDummy = nullptr; if (config.video.multisampling != 0 && Context::Multisampling) { m_pMSDummy = addFrameBufferTexture(textureTarget::TEXTURE_2D_MULTISAMPLE); // we don't want to remove dummy texture _initDummyTexture(m_pMSDummy); Context::InitTextureParams msParams; msParams.handle = m_pMSDummy->name; msParams.mipMapLevel = 0; msParams.msaaLevel = config.video.multisampling; msParams.width = m_pMSDummy->width; msParams.height = m_pMSDummy->height; msParams.format = colorFormat::RGBA; msParams.internalFormat = gfxContext.convertInternalTextureFormat(u32(internalcolorFormat::RGBA8)); msParams.dataType = datatype::UNSIGNED_BYTE; gfxContext.init2DTexture(msParams); activateMSDummy(0); activateMSDummy(1); } assert(!gfxContext.isError()); } void TextureCache::destroy() { current[0] = current[1] = nullptr; for (Textures::const_iterator cur = m_textures.cbegin(); cur != m_textures.cend(); ++cur) gfxContext.deleteTexture(cur->name); m_textures.clear(); m_lruTextureLocations.clear(); for (FBTextures::const_iterator cur = m_fbTextures.cbegin(); cur != m_fbTextures.cend(); ++cur) gfxContext.deleteTexture(cur->second.name); m_fbTextures.clear(); m_hdTexCacheSize = 0; } void TextureCache::_checkHdTexLimit() { const u64 maxCacheSize = config.textureFilter.txHiresVramLimit * 1024u * 1024u; // we don't need to do anything, // when the limit has been disabled if (maxCacheSize == 0u) return; // keep removing hd textures until we're below the max size for (auto iter = m_textures.rbegin(); iter != m_textures.rend() && m_hdTexCacheSize >= maxCacheSize;) { if (!iter->bHDTexture) { ++iter; } else { assert(m_hdTexCacheSize >= iter->textureBytes); m_hdTexCacheSize -= iter->textureBytes; gfxContext.deleteTexture(iter->name); m_lruTextureLocations.erase(iter->crc); iter = decltype(iter)(m_textures.erase(std::next(iter).base())); } } } void TextureCache::_checkCacheSize() { if (m_textures.size() >= m_maxCacheSize) { CachedTexture& clsTex = m_textures.back(); if (clsTex.bHDTexture) m_hdTexCacheSize -= clsTex.textureBytes; gfxContext.deleteTexture(clsTex.name); m_lruTextureLocations.erase(clsTex.crc); m_textures.pop_back(); } } CachedTexture * TextureCache::_addTexture(u64 _crc64) { if (m_curUnpackAlignment == 0) m_curUnpackAlignment = gfxContext.getTextureUnpackAlignment(); _checkCacheSize(); m_textures.emplace_front(gfxContext.createTexture(textureTarget::TEXTURE_2D)); Textures::iterator new_iter = m_textures.begin(); new_iter->crc = _crc64; m_lruTextureLocations.insert(std::pair(_crc64, new_iter)); return &(*new_iter); } void TextureCache::removeFrameBufferTexture(CachedTexture * _pTexture) { if (_pTexture == nullptr) return; FBTextures::const_iterator iter = m_fbTextures.find(u32(_pTexture->name)); assert(iter != m_fbTextures.cend()); if (iter != m_fbTextures.cend()) { gfxContext.deleteTexture(ObjectHandle(iter->second.name)); m_fbTextures.erase(iter); } } CachedTexture * TextureCache::addFrameBufferTexture(graphics::Parameter _target) { ObjectHandle texName(gfxContext.createTexture(_target)); m_fbTextures.emplace(u32(texName), texName); return &m_fbTextures.at(u32(texName)); } struct TileSizes { u32 clampWidth = 0U; u32 width = 0U; u32 clampHeight = 0U; u32 height = 0U; u32 bytes = 0U; }; static void _calcTileSizes(u32 _t, TileSizes & _sizes, gDPTile * _pLoadTile) { gDPTile * pTile = _t < 2 ? gSP.textureTile[_t] : &gDP.tiles[_t]; pTile->masks = pTile->originalMaskS; pTile->maskt = pTile->originalMaskT; u32 tileWidth = ((pTile->lrs - pTile->uls) & LOAD_BLOCK32_MASK) + 1; u32 tileHeight = ((pTile->lrt - pTile->ult) & LOAD_BLOCK32_MASK) + 1; const u32 tMemMask = gDP.otherMode.textureLUT == G_TT_NONE ? 0x1FF : 0xFF; gDPLoadTileInfo &info = gDP.loadInfo[pTile->tmem & tMemMask]; if (pTile->tmem == gDP.loadTile->tmem) { if (gDP.loadTile->loadWidth != 0 && gDP.loadTile->masks == 0) info.width = gDP.loadTile->loadWidth; if (gDP.loadTile->loadHeight != 0 && gDP.loadTile->maskt == 0) { info.height = gDP.loadTile->loadHeight; info.bytes = info.height * (gDP.loadTile->line << 3); if (gDP.loadTile->size == G_IM_SIZ_32b) // 32 bit texture loaded into lower and upper half of TMEM, thus actual bytes doubled. info.bytes *= 2; } } gDP.loadTile->loadWidth = gDP.loadTile->loadHeight = 0; _sizes.bytes = info.bytes; if (tileWidth == 1 && tileHeight == 1 && gDP.otherMode.cycleType == G_CYC_COPY && _pLoadTile != nullptr) { const u32 ulx = _SHIFTR(RDP.w1, 14, 10); const u32 uly = _SHIFTR(RDP.w1, 2, 10); const u32 lrx = _SHIFTR(RDP.w0, 14, 10); const u32 lry = _SHIFTR(RDP.w0, 2, 10); tileWidth = lrx - ulx + 1; tileHeight = lry - uly + 1; } u32 width = 0, height = 0; if (info.loadType == LOADTYPE_TILE) { width = min(info.width, info.texWidth); if (width == 0) width = tileWidth; if (info.size > pTile->size) width <<= info.size - pTile->size; height = info.height; if (height == 0) height = tileHeight; if ((config.generalEmulation.hacks & hack_MK64) != 0 && (height % 2) != 0) height--; } else { const TextureLoadParameters & loadParams = ImageFormat::get().tlp[gDP.otherMode.textureLUT][pTile->size][pTile->format]; int tile_width = pTile->lrs - pTile->uls + 1; int tile_height = pTile->lrt - pTile->ult + 1; int mask_width = (pTile->masks == 0) ? (tile_width) : (1 << pTile->masks); int mask_height = (pTile->maskt == 0) ? (tile_height) : (1 << pTile->maskt); if (pTile->clamps) width = min(mask_width, tile_width); else if ((u32)(mask_width * mask_height) <= loadParams.maxTexels) width = mask_width; else width = tileWidth; if (pTile->clampt) height = min(mask_height, tile_height); else if ((u32)(mask_width * mask_height) <= loadParams.maxTexels) height = mask_height; else height = tileHeight; } _sizes.clampWidth = (pTile->clamps && gDP.otherMode.cycleType != G_CYC_COPY) ? tileWidth : width; _sizes.clampHeight = (pTile->clampt && gDP.otherMode.cycleType != G_CYC_COPY) ? tileHeight : height; _sizes.width = (info.loadType == LOADTYPE_TILE && pTile->clamps != 0 && pTile->masks == 0) ? _sizes.clampWidth : width; _sizes.height = (info.loadType == LOADTYPE_TILE && pTile->clampt != 0 && pTile->maskt == 0) ? _sizes.clampHeight : height; } void TextureCache::_updateCachedTexture(const GHQTexInfo & _info, CachedTexture *_pTexture, u16 widthOrg, u16 heightOrg) { _pTexture->textureBytes = _info.width * _info.height; Parameter format(_info.format); if (format == internalcolorFormat::RGB8 || format == internalcolorFormat::RGBA4 || format == internalcolorFormat::RGB5_A1) { _pTexture->textureBytes <<= 1; } else { _pTexture->textureBytes <<= 2; } _pTexture->scaleS = 1.0f / (_pTexture->maskS ? f32(pow2(widthOrg)) : f32(widthOrg)); _pTexture->scaleT = 1.0f / (_pTexture->maskT ? f32(pow2(heightOrg)) : f32(heightOrg)); _pTexture->hdRatioS = (f32)(_info.width) / (f32)(_pTexture->width); _pTexture->hdRatioT = (f32)(_info.height) / (f32)(_pTexture->height); _pTexture->bHDTexture = true; m_hdTexCacheSize += _pTexture->textureBytes; _checkHdTexLimit(); } bool TextureCache::_loadHiresBackground(CachedTexture *_pTexture, u64 & _ricecrc) { if (!TFH.isInited()) return false; u8 * addr = (u8*)(RDRAM + gSP.bgImage.address); int tile_width = gSP.bgImage.width; int tile_height = gSP.bgImage.height; int bpl = tile_width << gSP.bgImage.size >> 1; u8 * paladdr = nullptr; u16 * palette = nullptr; if ((gSP.bgImage.size < G_IM_SIZ_16b) && (gDP.otherMode.textureLUT != G_TT_NONE || gSP.bgImage.format == G_IM_FMT_CI)) { if (gSP.bgImage.size == G_IM_SIZ_8b) paladdr = (u8*)(gDP.TexFilterPalette); else if (config.textureFilter.txHresAltCRC) paladdr = (u8*)(gDP.TexFilterPalette + (gSP.bgImage.palette << 5)); else paladdr = (u8*)(gDP.TexFilterPalette + (gSP.bgImage.palette << 4)); // TODO: fix palette load // palette = (rdp.pal_8 + (gSP.textureTile[_t]->palette << 4)); } _ricecrc = txfilter_checksum(addr, tile_width, tile_height, gSP.bgImage.size, bpl, paladdr); GHQTexInfo ghqTexInfo; // TODO: fix problem with zero texture dimensions on GLideNHQ side. if (txfilter_hirestex(_pTexture->crc, _ricecrc, palette, N64FormatSize(_pTexture->format, _pTexture->size), &ghqTexInfo) && ghqTexInfo.width != 0 && ghqTexInfo.height != 0) { ghqTexInfo.format = gfxContext.convertInternalTextureFormat(ghqTexInfo.format); Context::InitTextureParams params; params.handle = _pTexture->name; params.mipMapLevel = 0; params.msaaLevel = 0; params.width = ghqTexInfo.width; params.height = ghqTexInfo.height; params.format = ColorFormatParam(ghqTexInfo.texture_format); params.internalFormat = InternalColorFormatParam(ghqTexInfo.format); params.dataType = DatatypeParam(ghqTexInfo.pixel_type); params.data = ghqTexInfo.data; gfxContext.init2DTexture(params); assert(!gfxContext.isError()); _updateCachedTexture(ghqTexInfo, _pTexture, tile_width, tile_height); return true; } return false; } void TextureCache::_loadBackground(CachedTexture *pTexture) { u64 ricecrc = 0; if (_loadHiresBackground(pTexture, ricecrc)) return; u32 *pDest = nullptr; u16 *pDest16 = nullptr; u8 *pSwapped, *pSrc; u32 numBytes, bpl; u32 x, y, j, tx, ty; u16 clampSClamp; u16 clampTClamp; GetTexelFuncBG GetTexel; InternalColorFormatParam glInternalFormat; DatatypeParam glType; const TextureLoadParameters & loadParams = ImageFormat::get().tlp[pTexture->format == 2 ? G_TT_RGBA16 : G_TT_NONE][pTexture->size][pTexture->format]; if (loadParams.autoFormat == internalcolorFormat::RGBA8) { pTexture->textureBytes = (pTexture->width * pTexture->height) << 2; GetTexel = loadParams.Get32BG; glInternalFormat = loadParams.glInternalFormat32; glType = loadParams.glType32; } else { pTexture->textureBytes = (pTexture->width * pTexture->height) << 1; GetTexel = loadParams.Get16BG; glInternalFormat = loadParams.glInternalFormat16; glType = loadParams.glType16; } bpl = gSP.bgImage.width << gSP.bgImage.size >> 1; numBytes = bpl * gSP.bgImage.height; pSwapped = (u8*)malloc(numBytes); if (pSwapped == nullptr) return; UnswapCopyWrap(RDRAM, gSP.bgImage.address, pSwapped, 0, RDRAMSize, numBytes); pDest = (u32*)malloc(pTexture->textureBytes); if (pDest == nullptr) { free(pSwapped); return; } pDest16 = reinterpret_cast(pDest); clampSClamp = pTexture->width - 1; clampTClamp = pTexture->height - 1; j = 0; for (y = 0; y < pTexture->height; y++) { ty = min(y, (u32)clampTClamp); pSrc = &pSwapped[bpl * ty]; for (x = 0; x < pTexture->width; x++) { tx = min(x, (u32)clampSClamp); if (glInternalFormat == internalcolorFormat::RGBA8) pDest[j++] = GetTexel((u64*)pSrc, tx, 0, pTexture->palette); else pDest16[j++] = static_cast(GetTexel((u64*)pSrc, tx, 0, pTexture->palette)); } } if ((config.generalEmulation.hacks&hack_LoadDepthTextures) != 0 && gDP.colorImage.address == gDP.depthImageAddress) { _loadDepthTexture(pTexture, (u16*)pDest); free(pDest); free(pSwapped); return; } if (m_toggleDumpTex && config.textureFilter.txHiresEnable != 0 && config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) { txfilter_dmptx((u8*)pDest, pTexture->width, pTexture->height, pTexture->width, (u16)u32(glInternalFormat), N64FormatSize(pTexture->format, pTexture->size), ricecrc); } bool bLoaded = false; if ((config.textureFilter.txEnhancementMode | config.textureFilter.txFilterMode) != 0 && config.textureFilter.txFilterIgnoreBG == 0 && TFH.isInited()) { GHQTexInfo ghqTexInfo; if (txfilter_filter((u8*)pDest, pTexture->width, pTexture->height, (u16)u32(glInternalFormat), pTexture->crc, N64FormatSize(pTexture->format, pTexture->size), &ghqTexInfo) != 0 && ghqTexInfo.data != nullptr) { if (ghqTexInfo.width % 2 != 0 && ghqTexInfo.format != u32(internalcolorFormat::RGBA8) && m_curUnpackAlignment > 1) gfxContext.setTextureUnpackAlignment(2); ghqTexInfo.format = gfxContext.convertInternalTextureFormat(ghqTexInfo.format); Context::InitTextureParams params; params.handle = pTexture->name; params.mipMapLevel = 0; params.msaaLevel = 0; params.width = ghqTexInfo.width; params.height = ghqTexInfo.height; params.format = ColorFormatParam(ghqTexInfo.texture_format); params.internalFormat = InternalColorFormatParam(ghqTexInfo.format); params.dataType = DatatypeParam(ghqTexInfo.pixel_type); params.data = ghqTexInfo.data; gfxContext.init2DTexture(params); _updateCachedTexture(ghqTexInfo, pTexture, pTexture->width, pTexture->height); bLoaded = true; } } if (!bLoaded) { if (pTexture->width % 2 != 0 && glInternalFormat != internalcolorFormat::RGBA8) gfxContext.setTextureUnpackAlignment(2); Context::InitTextureParams params; params.handle = pTexture->name; params.mipMapLevel = 0; params.msaaLevel = 0; params.width = pTexture->width; params.height = pTexture->height; params.format = colorFormat::RGBA; params.internalFormat = gfxContext.convertInternalTextureFormat(u32(glInternalFormat)); params.dataType = glType; params.data = pDest; gfxContext.init2DTexture(params); } if (m_curUnpackAlignment > 1) gfxContext.setTextureUnpackAlignment(m_curUnpackAlignment); free(pSwapped); free(pDest); } bool TextureCache::_loadHiresTexture(u32 _tile, CachedTexture *_pTexture, u64 & _ricecrc) { if (config.textureFilter.txHiresEnable == 0 || !TFH.isInited()) return false; gDPLoadTileInfo & info = gDP.loadInfo[_pTexture->tMem]; // Temporal workaround for crash problem with mip-mapped textures. See #1711 for details. // TODO: make proper fix. if (info.texAddress == 0) return false; int bpl; int width, height; u8 * addr = (u8*)(RDRAM + info.texAddress); if (info.loadType == LOADTYPE_TILE) { bpl = info.texWidth << info.size >> 1; addr += (info.ult * bpl) + (((info.uls << info.size) + 1) >> 1); width = min(info.width, info.texWidth); if (info.size > _pTexture->size) width <<= info.size - _pTexture->size; height = info.height; if ((config.generalEmulation.hacks & hack_MK64) != 0 && (height % 2) != 0) height--; } else { const gDPTile * pTile = gSP.textureTile[_tile]; int tile_width = pTile->lrs - pTile->uls + 1; int tile_height = pTile->lrt - pTile->ult + 1; int mask_width = (pTile->masks == 0) ? (tile_width) : (1 << pTile->masks); int mask_height = (pTile->maskt == 0) ? (tile_height) : (1 << pTile->maskt); if ((pTile->clamps && tile_width <= 256)) width = min(mask_width, tile_width); else width = mask_width; if ((pTile->clampt && tile_height <= 256) || (mask_height > 256)) height = min(mask_height, tile_height); else height = mask_height; if (pTile->size == G_IM_SIZ_32b) bpl = pTile->line << 4; else if (info.dxt == 0) bpl = pTile->line << 3; else { u32 dxt = info.dxt; if (dxt > 1) dxt = ReverseDXT(dxt, info.width, _pTexture->width, _pTexture->size); bpl = dxt << 3; } } u8 * paladdr = nullptr; u16 * palette = nullptr; if ((_pTexture->size < G_IM_SIZ_16b) && (gDP.otherMode.textureLUT != G_TT_NONE || _pTexture->format == G_IM_FMT_CI)) { if (_pTexture->size == G_IM_SIZ_8b) paladdr = (u8*)(gDP.TexFilterPalette); else if (config.textureFilter.txHresAltCRC) paladdr = (u8*)(gDP.TexFilterPalette + (_pTexture->palette << 5)); else paladdr = (u8*)(gDP.TexFilterPalette + (_pTexture->palette << 4)); // TODO: fix palette load // palette = (rdp.pal_8 + (gSP.textureTile[_t]->palette << 4)); } _ricecrc = txfilter_checksum(addr, width, height, _pTexture->size, bpl, paladdr); GHQTexInfo ghqTexInfo; // TODO: fix problem with zero texture dimensions on GLideNHQ side. if (txfilter_hirestex(_pTexture->crc, _ricecrc, palette, N64FormatSize(_pTexture->format, _pTexture->size), &ghqTexInfo) && ghqTexInfo.width != 0 && ghqTexInfo.height != 0) { ghqTexInfo.format = gfxContext.convertInternalTextureFormat(ghqTexInfo.format); Context::InitTextureParams params; params.handle = _pTexture->name; params.mipMapLevel = 0; params.msaaLevel = 0; params.width = ghqTexInfo.width; params.height = ghqTexInfo.height; params.internalFormat = InternalColorFormatParam(ghqTexInfo.format); params.format = ColorFormatParam(ghqTexInfo.texture_format); params.dataType = DatatypeParam(ghqTexInfo.pixel_type); params.data = ghqTexInfo.data; params.textureUnitIndex = textureIndices::Tex[_tile]; gfxContext.init2DTexture(params); assert(!gfxContext.isError()); _updateCachedTexture(ghqTexInfo, _pTexture, width, height); return true; } return false; } void TextureCache::_loadDepthTexture(CachedTexture * _pTexture, u16* _pDest) { if (!config.generalEmulation.enableFragmentDepthWrite) return; u32 size = _pTexture->width * _pTexture->height; std::vector pDestFloat(size); for (u32 i = 0; i < size; ++i) pDestFloat[i] = _pDest[i] / 65535.0f; Context::InitTextureParams params; params.handle = _pTexture->name; params.mipMapLevel = 0; params.msaaLevel = 0; params.width = _pTexture->width; params.height = _pTexture->height; params.internalFormat = internalcolorFormat::R16F; params.format = colorFormat::RED; params.dataType = datatype::FLOAT; params.data = pDestFloat.data(); gfxContext.init2DTexture(params); } /* * Worker function for _load */ void TextureCache::_getTextureDestData(CachedTexture& tmptex, u32* pDest, Parameter glInternalFormat, GetTexelFunc GetTexel, u16* pLine) { u16 maskSMask, clampSClamp; u16 maskTMask, clampTClamp; u16 x, y, tx, ty; u32 i, j; if (tmptex.maskS > 0) { clampSClamp = tmptex.clampS ? tmptex.clampWidth - 1 : (tmptex.mirrorS ? (tmptex.width << 1) - 1 : tmptex.width - 1); maskSMask = (1 << tmptex.maskS) - 1; } else { clampSClamp = tmptex.clampS ? tmptex.clampWidth - 1 : tmptex.width - 1; maskSMask = 0xFFFF; } if (tmptex.maskT > 0) { clampTClamp = tmptex.clampT ? tmptex.clampHeight - 1 : (tmptex.mirrorT ? (tmptex.height << 1) - 1 : tmptex.height - 1); maskTMask = (1 << tmptex.maskT) - 1; } else { clampTClamp = tmptex.clampT ? tmptex.clampHeight - 1 : tmptex.height - 1; maskTMask = 0xFFFF; } if (tmptex.size == G_IM_SIZ_32b) { const u16 * tmem16 = (u16*)TMEM; const u32 tbase = tmptex.tMem << 2; int wid_64 = (tmptex.clampWidth) << 2; if (wid_64 & 15) { wid_64 += 16; } wid_64 &= 0xFFFFFFF0; wid_64 >>= 3; int line32 = tmptex.line << 1; line32 = (line32 - wid_64) << 3; if (wid_64 < 1) { wid_64 = 1; } int width = wid_64 << 1; line32 = width + (line32 >> 2); u16 gr, ab; j = 0; for (y = 0; y < tmptex.height; ++y) { ty = min(y, clampTClamp) & maskTMask; u32 tline = tbase + line32 * ty; u32 xorval = (ty & 1) ? 3 : 1; for (x = 0; x < tmptex.width; ++x) { tx = min(x, clampSClamp) & maskSMask; u32 taddr = ((tline + tx) ^ xorval) & LOAD_BLOCK32_MASK; #ifdef NATIVE gr = tmem16[taddr]; ab = tmem16[taddr | LOAD_BLOCK32_MAX]; #else gr = swapword(tmem16[taddr]); ab = swapword(tmem16[taddr | LOAD_BLOCK32_MAX]); #endif pDest[j++] = (ab << 16) | gr; } } } else if (tmptex.format == G_IM_FMT_YUV) { j = 0; *pLine <<= 1; for (y = 0; y < tmptex.height; ++y) { u64* pSrc = &TMEM[tmptex.tMem] + *pLine * y; for (x = 0; x < tmptex.width / 2; x++) { GetYUV_RGBA8888(pSrc, pDest + j, x); j += 2; } } } else { j = 0; const u32 tMemMask = gDP.otherMode.textureLUT == G_TT_NONE ? 0x1FF : 0xFF; for (y = 0; y < tmptex.height; ++y) { ty = min(y, clampTClamp) & maskTMask; u16 tmemOffset = (tmptex.tMem + *pLine * ty) & tMemMask; i = (ty & 1) << 1; for (x = 0; x < tmptex.width; ++x) { tx = min(x, clampSClamp) & maskSMask; if (glInternalFormat == internalcolorFormat::RGBA8) pDest[j++] = GetTexel(tmemOffset, tx, i, tmptex.palette); else ((u16*)pDest)[j++] = GetTexel(tmemOffset, tx, i, tmptex.palette); } } } } template void doubleTexture(T* pTex, u32 width, u32 height) { std::vector vData(width * height); memcpy(vData.data(), pTex, width * height * sizeof(T)); u32 srcIdx = 0; u32 dstIdx = 0; for (u32 y = 0; y < height; ++y) { const u32 srcIdxCur = srcIdx; for (u32 x = 0; x < width; ++x) { pTex[dstIdx++] = vData[srcIdx]; pTex[dstIdx++] = vData[srcIdx++]; } srcIdx = srcIdxCur; for (u32 x = 0; x < width; ++x) { pTex[dstIdx++] = vData[srcIdx]; pTex[dstIdx++] = vData[srcIdx++]; } } } void TextureCache::_loadFast(u32 _tile, CachedTexture *_pTexture) { u64 ricecrc = 0; if (_loadHiresTexture(_tile, _pTexture, ricecrc)) return; s32 mipLevel = 0; bool force32bitFormat = false; _pTexture->max_level = 0; if (config.generalEmulation.enableLOD != 0 && gSP.texture.level > 1) { if (_tile == 0) { _pTexture->max_level = 0; } else { _pTexture->max_level = static_cast(gSP.texture.level - 1); const u16 dim = std::max(_pTexture->width, _pTexture->height); while (dim < static_cast(1 << _pTexture->max_level)) --_pTexture->max_level; auto texFormat = gDP.tiles[gSP.texture.tile + 1].format; auto texSize = gDP.tiles[gSP.texture.tile + 1].size; u32 tileMipLevel = gSP.texture.tile + 2; while (!force32bitFormat && (tileMipLevel < gSP.texture.tile + gSP.texture.level)) { gDPTile const& mipTile = gDP.tiles[tileMipLevel++]; force32bitFormat = texFormat != mipTile.format || texSize != mipTile.size; } } } u32 sizeShift = 1; { const TextureLoadParameters & loadParams = ImageFormat::get().tlp[gDP.otherMode.textureLUT][_pTexture->size][_pTexture->format]; if (force32bitFormat || loadParams.autoFormat == internalcolorFormat::RGBA8) sizeShift = 2; } _pTexture->textureBytes = (_pTexture->width * _pTexture->height) << sizeShift; unsigned int totalTexSize = std::max(static_cast(_pTexture->textureBytes/sizeof(u32) + 8), MIPMAP_TILE_WIDTH) * (_pTexture->max_level + 1); if (m_tempTextureHolder.size() < totalTexSize) { m_tempTextureHolder.resize(totalTexSize); } GetTexelFunc GetTexel; InternalColorFormatParam glInternalFormat; DatatypeParam glType; auto getLoadParams = [&](u16 _format, u16 _size) { const TextureLoadParameters & loadParams = ImageFormat::get().tlp[gDP.otherMode.textureLUT][_size][_format]; if (force32bitFormat || loadParams.autoFormat == internalcolorFormat::RGBA8) { GetTexel = loadParams.Get32; glInternalFormat = loadParams.glInternalFormat32; glType = loadParams.glType32; } else { GetTexel = loadParams.Get16; glInternalFormat = loadParams.glInternalFormat16; glType = loadParams.glType16; } }; CachedTexture tmptex = *_pTexture; u16 line = tmptex.line; while (true) { getLoadParams(tmptex.format, tmptex.size); { const u32 tileMipLevel = gSP.texture.tile + mipLevel + 1; gDPTile & mipTile = gDP.tiles[tileMipLevel]; if (tmptex.max_level > 1 && tmptex.width == (mipTile.lrs - mipTile.uls + 1) * 2 && tmptex.height == (mipTile.lrt - mipTile.ult + 1) * 2) { // Special case for Southern Swamp grass texture, Zelda MM. See #2315 const u16 texWidth = tmptex.width; const u16 texHeight = tmptex.height; tmptex.width = mipTile.lrs - mipTile.uls + 1; tmptex.height = mipTile.lrt - mipTile.ult + 1; _getTextureDestData(tmptex, m_tempTextureHolder.data(), glInternalFormat, GetTexel, &line); if (sizeShift == 2) doubleTexture(m_tempTextureHolder.data(), tmptex.width, tmptex.height); else doubleTexture((u16*)m_tempTextureHolder.data(), tmptex.width, tmptex.height); tmptex.width = texWidth; tmptex.height = texHeight; } else { _getTextureDestData(tmptex, m_tempTextureHolder.data(), glInternalFormat, GetTexel, &line); } } if ((config.generalEmulation.hacks&hack_LoadDepthTextures) != 0 && gDP.colorImage.address == gDP.depthImageAddress) { _loadDepthTexture(_pTexture, (u16*)m_tempTextureHolder.data()); return; } if (m_toggleDumpTex && config.textureFilter.txHiresEnable != 0 && config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) { txfilter_dmptx((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, tmptex.width, (u16)u32(glInternalFormat), N64FormatSize(_pTexture->format, _pTexture->size), ricecrc); } bool bLoaded = false; bool needEnhance = (config.textureFilter.txEnhancementMode | config.textureFilter.txFilterMode) != 0 && _pTexture->max_level == 0 && TFH.isInited(); if (needEnhance) { if (config.textureFilter.txFilterIgnoreBG != 0) { switch (GBI.getMicrocodeType()) { case S2DEX_1_07: case S2DEX_1_03: case S2DEX_1_05: needEnhance = RSP.cmd != 0x01 && RSP.cmd != 0x02; break; case S2DEX2: needEnhance = RSP.cmd != 0x09 && RSP.cmd != 0x0A; break; } } } if (needEnhance) { GHQTexInfo ghqTexInfo; if (txfilter_filter((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, (u16)u32(glInternalFormat), (uint64)_pTexture->crc, N64FormatSize(_pTexture->format, _pTexture->size), &ghqTexInfo) != 0 && ghqTexInfo.data != nullptr) { if (ghqTexInfo.width % 2 != 0 && ghqTexInfo.format != u32(internalcolorFormat::RGBA8) && m_curUnpackAlignment > 1) gfxContext.setTextureUnpackAlignment(2); ghqTexInfo.format = gfxContext.convertInternalTextureFormat(ghqTexInfo.format); Context::InitTextureParams params; params.handle = _pTexture->name; params.textureUnitIndex = textureIndices::Tex[_tile]; params.mipMapLevel = 0; params.msaaLevel = 0; params.width = ghqTexInfo.width; params.height = ghqTexInfo.height; params.internalFormat = InternalColorFormatParam(ghqTexInfo.format); params.format = ColorFormatParam(ghqTexInfo.texture_format); params.dataType = DatatypeParam(ghqTexInfo.pixel_type); params.data = ghqTexInfo.data; gfxContext.init2DTexture(params); _updateCachedTexture(ghqTexInfo, _pTexture, tmptex.width, tmptex.height); bLoaded = true; } } if (!bLoaded) { if (tmptex.width % 2 != 0 && glInternalFormat != internalcolorFormat::RGBA8 && m_curUnpackAlignment > 1) gfxContext.setTextureUnpackAlignment(2); Context::InitTextureParams params; params.handle = _pTexture->name; params.textureUnitIndex = textureIndices::Tex[_tile]; params.mipMapLevel = mipLevel; params.mipMapLevels = _pTexture->max_level + 1; params.msaaLevel = 0; params.width = tmptex.width; params.height = tmptex.height; params.internalFormat = gfxContext.convertInternalTextureFormat(u32(glInternalFormat)); params.format = colorFormat::RGBA; params.dataType = glType; params.data = m_tempTextureHolder.data(); gfxContext.init2DTexture(params); } if (mipLevel == _pTexture->max_level) break; ++mipLevel; const u32 tileMipLevel = gSP.texture.tile + mipLevel + 1; gDPTile & mipTile = gDP.tiles[tileMipLevel]; line = mipTile.line; tmptex.tMem = mipTile.tmem; tmptex.palette = mipTile.palette; tmptex.maskS = mipTile.masks; tmptex.maskT = mipTile.maskt; tmptex.format = mipTile.format; tmptex.size = mipTile.size; TileSizes sizes; _calcTileSizes(tileMipLevel, sizes, nullptr); tmptex.clampWidth = sizes.clampWidth; tmptex.clampHeight = sizes.clampHeight; // Insure mip-map levels size consistency. if (tmptex.width > 1) tmptex.width >>= 1; if (tmptex.height > 1) tmptex.height >>= 1; _pTexture->textureBytes += (tmptex.width * tmptex.height) << sizeShift; } if (m_curUnpackAlignment > 1) gfxContext.setTextureUnpackAlignment(m_curUnpackAlignment); } void TextureCache::_loadAccurate(u32 _tile, CachedTexture *_pTexture) { u64 ricecrc = 0; if (_loadHiresTexture(_tile, _pTexture, ricecrc)) return; bool force32bitFormat = false; _pTexture->max_level = 0; if (currentCombiner()->usesLOD() && gSP.texture.level > 1 && _tile > 0) { _pTexture->max_level = gDP.otherMode.textureDetail == G_TD_DETAIL ? static_cast(gSP.texture.level) : static_cast(gSP.texture.level - 1); force32bitFormat = _pTexture->max_level > 0; } u32 sizeShift = 1; { const TextureLoadParameters & loadParams = ImageFormat::get().tlp[gDP.otherMode.textureLUT][_pTexture->size][_pTexture->format]; if (force32bitFormat || loadParams.autoFormat == internalcolorFormat::RGBA8) sizeShift = 2; } _pTexture->textureBytes = (_pTexture->width * _pTexture->height) << sizeShift; unsigned int totalTexSize = std::max(static_cast(_pTexture->textureBytes/sizeof(u32) + 8), MIPMAP_TILE_WIDTH) * (_pTexture->max_level + 1); if (m_tempTextureHolder.size() < totalTexSize) { m_tempTextureHolder.resize(totalTexSize); } GetTexelFunc GetTexel; InternalColorFormatParam glInternalFormat; DatatypeParam glType; auto getLoadParams = [&](u16 _format, u16 _size) { const TextureLoadParameters & loadParams = ImageFormat::get().tlp[gDP.otherMode.textureLUT][_size][_format]; if (force32bitFormat || loadParams.autoFormat == internalcolorFormat::RGBA8) { GetTexel = loadParams.Get32; glInternalFormat = loadParams.glInternalFormat32; glType = loadParams.glType32; } else { GetTexel = loadParams.Get16; glInternalFormat = loadParams.glInternalFormat16; glType = loadParams.glType16; } }; CachedTexture tmptex = *_pTexture; u16 line = tmptex.line; if (_pTexture->max_level > 0) { u32 mipLevel = 0; u32 texDataOffset = 8; // number of gDP.tiles // Load all tiles into one 1D texture atlas. while (true) { u32 mipRatioS = gDP.tiles[gSP.texture.tile + mipLevel + 1].shifts + 5u; if (mipRatioS >= 16u) mipRatioS -= 16u; u32 mipRatioT = gDP.tiles[gSP.texture.tile + mipLevel + 1].shiftt + 5u; if (mipRatioT >= 16) mipRatioT -= 16u; const u32 tileSizePacked = texDataOffset | (tmptex.width << 16) | (mipRatioT << 24) | (mipRatioS << 28); m_tempTextureHolder[mipLevel] = tileSizePacked; getLoadParams(tmptex.format, tmptex.size); _getTextureDestData(tmptex, &m_tempTextureHolder[texDataOffset], glInternalFormat, GetTexel, &line); if (m_toggleDumpTex && config.textureFilter.txHiresEnable != 0 && config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) { txfilter_dmptx((u8*)(m_tempTextureHolder.data() + texDataOffset), tmptex.width, tmptex.height, tmptex.width, (u16)u32(glInternalFormat), N64FormatSize(_pTexture->format, _pTexture->size), ricecrc); } texDataOffset += tmptex.width * tmptex.height; if (mipLevel == _pTexture->max_level) break; ++mipLevel; const u32 tileMipLevel = gSP.texture.tile + mipLevel + 1; gDPTile & mipTile = gDP.tiles[tileMipLevel]; gDPTile & prevMipTile = gDP.tiles[tileMipLevel - 1]; line = mipTile.line; tmptex.tMem = mipTile.tmem; tmptex.palette = mipTile.palette; tmptex.maskS = mipTile.masks; tmptex.maskT = mipTile.maskt; tmptex.format = mipTile.format; tmptex.size = mipTile.size; TileSizes sizes; _calcTileSizes(tileMipLevel, sizes, nullptr); tmptex.width = std::min(tmptex.width, static_cast(sizes.width)); tmptex.height = std::min(tmptex.height, static_cast(sizes.height)); tmptex.clampWidth = sizes.clampWidth; tmptex.clampHeight = sizes.clampHeight; _pTexture->textureBytes += (tmptex.width * tmptex.height) << sizeShift; } Context::InitTextureParams params; params.handle = _pTexture->name; params.textureUnitIndex = textureIndices::Tex[_tile]; params.mipMapLevel = 0; params.mipMapLevels =1; params.msaaLevel = 0; params.width = std::min(texDataOffset, MIPMAP_TILE_WIDTH); params.height = (texDataOffset / MIPMAP_TILE_WIDTH) + ((texDataOffset % MIPMAP_TILE_WIDTH) ? 1 : 0); params.internalFormat = gfxContext.convertInternalTextureFormat(u32(glInternalFormat)); params.format = colorFormat::RGBA; params.dataType = glType; params.data = m_tempTextureHolder.data(); gfxContext.init2DTexture(params); _pTexture->mipmapAtlasWidth = params.width; _pTexture->mipmapAtlasHeight = params.height; } else { getLoadParams(tmptex.format, tmptex.size); _getTextureDestData(tmptex, m_tempTextureHolder.data(), glInternalFormat, GetTexel, &line); if ((config.generalEmulation.hacks&hack_LoadDepthTextures) != 0 && gDP.colorImage.address == gDP.depthImageAddress) { _loadDepthTexture(_pTexture, (u16*)m_tempTextureHolder.data()); return; } if (m_toggleDumpTex && config.textureFilter.txHiresEnable != 0 && config.hotkeys.enabledKeys[Config::HotKey::hkTexDump] != 0) { txfilter_dmptx((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, tmptex.width, (u16)u32(glInternalFormat), N64FormatSize(_pTexture->format, _pTexture->size), ricecrc); } bool bLoaded = false; bool needEnhance = (config.textureFilter.txEnhancementMode | config.textureFilter.txFilterMode) != 0 && _pTexture->max_level == 0 && TFH.isInited(); if (needEnhance) { if (config.textureFilter.txFilterIgnoreBG != 0) { switch (GBI.getMicrocodeType()) { case S2DEX_1_07: case S2DEX_1_03: case S2DEX_1_05: needEnhance = RSP.cmd != 0x01 && RSP.cmd != 0x02; break; case S2DEX2: needEnhance = RSP.cmd != 0x09 && RSP.cmd != 0x0A; break; } } } if (needEnhance) { GHQTexInfo ghqTexInfo; if (txfilter_filter((u8*)m_tempTextureHolder.data(), tmptex.width, tmptex.height, (u16)u32(glInternalFormat), (uint64)_pTexture->crc, N64FormatSize(_pTexture->format, _pTexture->size), &ghqTexInfo) != 0 && ghqTexInfo.data != nullptr) { if (ghqTexInfo.width % 2 != 0 && ghqTexInfo.format != u32(internalcolorFormat::RGBA8) && m_curUnpackAlignment > 1) gfxContext.setTextureUnpackAlignment(2); ghqTexInfo.format = gfxContext.convertInternalTextureFormat(ghqTexInfo.format); Context::InitTextureParams params; params.handle = _pTexture->name; params.textureUnitIndex = textureIndices::Tex[_tile]; params.mipMapLevel = 0; params.msaaLevel = 0; params.width = ghqTexInfo.width; params.height = ghqTexInfo.height; params.internalFormat = InternalColorFormatParam(ghqTexInfo.format); params.format = ColorFormatParam(ghqTexInfo.texture_format); params.dataType = DatatypeParam(ghqTexInfo.pixel_type); params.data = ghqTexInfo.data; gfxContext.init2DTexture(params); _updateCachedTexture(ghqTexInfo, _pTexture, tmptex.width, tmptex.height); bLoaded = true; } } if (!bLoaded) { if (tmptex.width % 2 != 0 && glInternalFormat != internalcolorFormat::RGBA8 && m_curUnpackAlignment > 1) gfxContext.setTextureUnpackAlignment(2); Context::InitTextureParams params; params.handle = _pTexture->name; params.textureUnitIndex = textureIndices::Tex[_tile]; params.mipMapLevel = 0; params.mipMapLevels = 1; params.msaaLevel = 0; params.width = tmptex.width; params.height = tmptex.height; params.internalFormat = gfxContext.convertInternalTextureFormat(u32(glInternalFormat)); params.format = colorFormat::RGBA; params.dataType = glType; params.data = m_tempTextureHolder.data(); gfxContext.init2DTexture(params); } } if (m_curUnpackAlignment > 1) gfxContext.setTextureUnpackAlignment(m_curUnpackAlignment); } struct TextureParams { u16 width; u16 height; u32 flags; }; static u64 _calculateCRC(u32 _t, const TextureParams & _params, u32 _bytes) { const bool rgba32 = gSP.textureTile[_t]->size == G_IM_SIZ_32b; if (_bytes == 0) { const u32 lineBytes = gSP.textureTile[_t]->line << 3; _bytes = _params.height*lineBytes; } if (rgba32) _bytes >>= 1; const u32 tMemMask = (gDP.otherMode.textureLUT == G_TT_NONE && !rgba32) ? 0x1FF : 0xFF; const u32 tMem = gSP.textureTile[_t]->tmem & tMemMask; const u64 *src = (u64*)&TMEM[tMem]; const u32 maxBytes = (tMemMask + 1) << 3; const u32 tileTmemInBytes = tMem << 3; if (!rgba32 && (tileTmemInBytes + _bytes > maxBytes)) _bytes = maxBytes - tileTmemInBytes; u64 crc = UINT64_MAX; crc = CRC_Calculate(crc, src, _bytes); if (rgba32) { src = (u64*)&TMEM[(gSP.textureTile[_t]->tmem + 256) & 0x1FF]; crc = CRC_Calculate(crc, src, _bytes); } if (gDP.otherMode.textureLUT != G_TT_NONE || gSP.textureTile[_t]->format == G_IM_FMT_CI) { if (gSP.textureTile[_t]->size == G_IM_SIZ_4b) crc = CRC_Calculate( crc, &gDP.paletteCRC16[gSP.textureTile[_t]->palette], sizeof(u64) ); else if (gSP.textureTile[_t]->size == G_IM_SIZ_8b) crc = CRC_Calculate( crc, &gDP.paletteCRC256, sizeof(u64) ); } if (config.generalEmulation.enableLOD != 0 && gSP.texture.level > 1 && _t > 0) crc = CRC_Calculate(crc, &gSP.texture.level, 4); crc = CRC_Calculate(crc, &_params, sizeof(_params)); return crc; } void TextureCache::activateTexture(u32 _t, CachedTexture *_pTexture) { Context::TexParameters params; params.handle = _pTexture->name; if (config.video.multisampling > 0 && _pTexture->frameBufferTexture == CachedTexture::fbMultiSample) { params.target = textureTarget::TEXTURE_2D_MULTISAMPLE; params.textureUnitIndex = textureIndices::MSTex[_t]; } else { params.target = textureTarget::TEXTURE_2D; params.textureUnitIndex = textureIndices::Tex[_t]; params.minFilter = textureParameters::FILTER_NEAREST; params.magFilter = textureParameters::FILTER_NEAREST; params.maxMipmapLevel = Parameter(0); if (config.generalEmulation.enableInaccurateTextureCoordinates != 0) { const bool bUseBilinear = gDP.otherMode.textureFilter != G_TF_POINT && config.texture.bilinearMode != BILINEAR_3POINT; const bool bUseLOD = currentCombiner()->usesLOD(); const s32 texLevel = bUseLOD ? _pTexture->max_level : 0; params.maxMipmapLevel = Parameter(texLevel); if (bUseLOD) { if (bUseBilinear) { // Apply standard bilinear to mipmap textures if (texLevel > 0) params.minFilter = textureParameters::FILTER_LINEAR_MIPMAP_NEAREST; else params.minFilter = textureParameters::FILTER_LINEAR; params.magFilter = textureParameters::FILTER_LINEAR; } else { if (texLevel > 0) params.minFilter = textureParameters::FILTER_NEAREST_MIPMAP_NEAREST; else params.minFilter = textureParameters::FILTER_NEAREST; params.magFilter = textureParameters::FILTER_NEAREST; } } // Set clamping modes params.wrapS = _pTexture->clampS ? textureParameters::WRAP_CLAMP_TO_EDGE : _pTexture->mirrorS ? textureParameters::WRAP_MIRRORED_REPEAT : textureParameters::WRAP_REPEAT; params.wrapT = _pTexture->clampT ? textureParameters::WRAP_CLAMP_TO_EDGE : _pTexture->mirrorT ? textureParameters::WRAP_MIRRORED_REPEAT : textureParameters::WRAP_REPEAT; } if (config.texture.anisotropy != 0) { switch (dwnd().getDrawer().getDrawingState()) { case DrawingState::Triangle: case DrawingState::ScreenSpaceTriangle: params.maxAnisotropy = Parameter(static_cast(config.texture.anisotropy)); break; default: break; } } } gfxContext.setTextureParameters(params); current[_t] = _pTexture; } void TextureCache::activateDummy(u32 _t) { Context::TexParameters params; params.handle = m_pDummy->name; params.target = textureTarget::TEXTURE_2D; params.textureUnitIndex = textureIndices::Tex[_t]; params.minFilter = textureParameters::FILTER_NEAREST; params.magFilter = textureParameters::FILTER_NEAREST; gfxContext.setTextureParameters(params); } void TextureCache::activateMSDummy(u32 _t) { Context::TexParameters params; params.handle = m_pMSDummy->name; params.target = textureTarget::TEXTURE_2D_MULTISAMPLE; params.textureUnitIndex = textureIndices::MSTex[_t]; gfxContext.setTextureParameters(params); } void TextureCache::_updateBackground() { u32 numBytes = gSP.bgImage.width * gSP.bgImage.height << gSP.bgImage.size >> 1; u64 crc; crc = CRC_Calculate( UINT64_MAX, &RDRAM[gSP.bgImage.address], numBytes ); if (gDP.otherMode.textureLUT != G_TT_NONE || gSP.bgImage.format == G_IM_FMT_CI) { if (gSP.bgImage.size == G_IM_SIZ_4b) crc = CRC_Calculate( crc, &gDP.paletteCRC16[gSP.bgImage.palette], sizeof(u64) ); else if (gSP.bgImage.size == G_IM_SIZ_8b) crc = CRC_Calculate( crc, &gDP.paletteCRC256, sizeof(u64) ); } u32 params[4] = {gSP.bgImage.width, gSP.bgImage.height, gSP.bgImage.format, gSP.bgImage.size}; crc = CRC_Calculate(crc, params, sizeof(u32)*4); Texture_Locations::iterator locations_iter = m_lruTextureLocations.find(crc); if (locations_iter != m_lruTextureLocations.end()) { Textures::iterator iter = locations_iter->second; CachedTexture & currentTex = *iter; m_textures.splice(m_textures.begin(), m_textures, iter); assert(currentTex.width == gSP.bgImage.width); assert(currentTex.height == gSP.bgImage.height); assert(currentTex.format == gSP.bgImage.format); assert(currentTex.size == gSP.bgImage.size); currentTex.clampS = gSP.bgImage.clampS; currentTex.clampT = gSP.bgImage.clampT; activateTexture(0, ¤tTex); m_hits++; return; } m_misses++; CachedTexture * pCurrent = _addTexture(crc); pCurrent->address = gSP.bgImage.address; pCurrent->format = gSP.bgImage.format; pCurrent->size = gSP.bgImage.size; pCurrent->width = gSP.bgImage.width; pCurrent->height = gSP.bgImage.height; pCurrent->clampWidth = gSP.bgImage.width; pCurrent->clampHeight = gSP.bgImage.height; pCurrent->palette = gSP.bgImage.palette; pCurrent->maskS = 0; pCurrent->maskT = 0; pCurrent->mirrorS = 0; pCurrent->mirrorT = 0; pCurrent->clampS = gSP.bgImage.clampS; pCurrent->clampT = gSP.bgImage.clampT; pCurrent->line = 0; pCurrent->tMem = 0; pCurrent->frameBufferTexture = CachedTexture::fbNone; pCurrent->scaleS = 1.0f / (f32)(pCurrent->width); pCurrent->scaleT = 1.0f / (f32)(pCurrent->height); pCurrent->hdRatioS = 1.0f; pCurrent->hdRatioT = 1.0f; pCurrent->shiftScaleS = 1.0f; pCurrent->shiftScaleT = 1.0f; pCurrent->offsetS = 0.0f; pCurrent->offsetT = 0.0f; _loadBackground(pCurrent); activateTexture(0, pCurrent); current[0] = pCurrent; } void TextureCache::clear() { current[0] = current[1] = nullptr; for (auto cur = m_textures.cbegin(); cur != m_textures.cend(); ++cur) { gfxContext.deleteTexture(cur->name); } m_textures.clear(); m_lruTextureLocations.clear(); m_hdTexCacheSize = 0u; } void TextureCache::toggleDumpTex() { m_toggleDumpTex = !m_toggleDumpTex; if (m_toggleDumpTex) { displayLoadProgress(L"Texture dump - ON\n"); clear(); std::this_thread::sleep_for(std::chrono::seconds(1)); } else { displayLoadProgress(L"Texture dump - OFF\n"); std::this_thread::sleep_for(std::chrono::seconds(1)); } } void TextureCache::update(u32 _t) { const gDPTile * pTile = gSP.textureTile[_t]; switch (pTile->textureMode) { case TEXTUREMODE_BGIMAGE: _updateBackground(); return; case TEXTUREMODE_FRAMEBUFFER: FrameBuffer_ActivateBufferTexture( _t, pTile->frameBufferAddress ); return; case TEXTUREMODE_FRAMEBUFFER_BG: FrameBuffer_ActivateBufferTextureBG( _t, pTile->frameBufferAddress ); return; } if (_t == 1 && needReplaceTex1ByTex0()) { current[1] = current[0]; if (current[1] != nullptr) { activateTexture(1, current[1]); return; } } if (gSP.texture.tile == 7 && _t == 0 && gSP.textureTile[0] == gDP.loadTile && gDP.loadTile->loadType == LOADTYPE_BLOCK && gSP.textureTile[0]->tmem == gSP.textureTile[1]->tmem) { gSP.textureTile[0] = gSP.textureTile[1]; pTile = gSP.textureTile[_t]; } TileSizes sizes; _calcTileSizes(_t, sizes, gDP.loadTile); TextureParams params; const u32 texLevel = _t == 0 ? 0U : gSP.texture.level; params.flags = pTile->masks | (pTile->maskt << 4) | (pTile->mirrors << 8) | (pTile->mirrort << 9) | (pTile->clamps << 10) | (pTile->clampt << 11) | (pTile->size << 12) | (pTile->format << 14) | (gDP.otherMode.textureLUT << 17) | (texLevel << 19); params.width = sizes.width; params.height = sizes.height; const u64 crc = _calculateCRC(_t, params, sizes.bytes); if (current[_t] != nullptr && current[_t]->crc == crc) { activateTexture(_t, current[_t]); return; } Texture_Locations::iterator locations_iter = m_lruTextureLocations.find(crc); if (locations_iter != m_lruTextureLocations.end()) { Textures::iterator iter = locations_iter->second; CachedTexture & currentTex = *iter; if (currentTex.width == sizes.width && currentTex.height == sizes.height) { m_textures.splice(m_textures.begin(), m_textures, iter); assert(currentTex.format == pTile->format); assert(currentTex.size == pTile->size); activateTexture(_t, ¤tTex); m_hits++; return; } if (currentTex.bHDTexture) m_hdTexCacheSize -= currentTex.textureBytes; gfxContext.deleteTexture(currentTex.name); m_lruTextureLocations.erase(locations_iter); m_textures.erase(iter); } m_misses++; CachedTexture * pCurrent = _addTexture(crc); pCurrent->address = gDP.loadInfo[pTile->tmem].texAddress; pCurrent->format = pTile->format; pCurrent->size = pTile->size; pCurrent->width = sizes.width; pCurrent->height = sizes.height; pCurrent->clampWidth = sizes.clampWidth; pCurrent->clampHeight = sizes.clampHeight; pCurrent->palette = pTile->palette; /* pCurrent->fulS = gSP.textureTile[t]->fulS; pCurrent->fulT = gSP.textureTile[t]->fulT; pCurrent->ulS = gSP.textureTile[t]->ulS; pCurrent->ulT = gSP.textureTile[t]->ulT; pCurrent->lrS = gSP.textureTile[t]->lrS; pCurrent->lrT = gSP.textureTile[t]->lrT;*/ pCurrent->maskS = pTile->masks; pCurrent->maskT = pTile->maskt; pCurrent->mirrorS = pTile->mirrors; pCurrent->mirrorT = pTile->mirrort; pCurrent->clampS = pTile->clamps; pCurrent->clampT = pTile->clampt; pCurrent->line = pTile->line; pCurrent->tMem = pTile->tmem; pCurrent->frameBufferTexture = CachedTexture::fbNone; pCurrent->scaleS = 1.0f / (pCurrent->maskS ? f32(pow2(pCurrent->width)) : f32(pCurrent->width)); pCurrent->scaleT = 1.0f / (pCurrent->maskT ? f32(pow2(pCurrent->height)) : f32(pCurrent->height)); pCurrent->hdRatioS = 1.0f; pCurrent->hdRatioT = 1.0f; pCurrent->offsetS = 0.0f; pCurrent->offsetT = 0.0f; if (config.generalEmulation.enableInaccurateTextureCoordinates) { _loadFast(_t, pCurrent); } else { _loadAccurate(_t, pCurrent); } activateTexture( _t, pCurrent ); current[_t] = pCurrent; } void getTextureShiftScale(u32 t, const TextureCache & cache, f32 & shiftScaleS, f32 & shiftScaleT) { gDPTile & tile = *gSP.textureTile[t]; if (tile.textureMode != TEXTUREMODE_NORMAL) { shiftScaleS = cache.current[t]->shiftScaleS; shiftScaleT = cache.current[t]->shiftScaleT; return; } if (gDP.otherMode.textureLOD == G_TL_LOD && gSP.texture.level == 0 && !currentCombiner()->usesLOD()) t = 0; shiftScaleS = calcShiftScaleS(tile); shiftScaleT = calcShiftScaleT(tile); } bool needReplaceTex1ByTex0() { return config.generalEmulation.enableInaccurateTextureCoordinates && gSP.texture.level == 0 && gDP.otherMode.textureLOD == G_TL_LOD && gDP.otherMode.textureDetail == G_TD_CLAMP; }