/* This file is part of Flycast. Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ #include "texconv.h" #include "cfg/option.h" #include "hw/pvr/Renderer_if.h" #include #include const u8 *vq_codebook; u32 palette_index; u32 palette16_ram[1024]; u32 palette32_ram[1024]; u32 pal_hash_256[4]; u32 pal_hash_16[64]; extern bool pal_needs_update; u32 detwiddle[2][11][1024]; //input : address in the yyyyyxxxxx format //output : address in the xyxyxyxy format //U : x resolution , V : y resolution //twiddle works on 64b words static u32 twiddle_slow(u32 x, u32 y, u32 x_sz, u32 y_sz) { u32 rv = 0; // low 2 bits are directly passed -> needs some misc stuff to work.However // Pvr internally maps the 64b banks "as if" they were twiddled u32 sh = 0; x_sz >>= 1; y_sz >>= 1; while (x_sz != 0 || y_sz != 0) { if (y_sz != 0) { u32 temp = y & 1; rv |= temp << sh; y_sz >>= 1; y >>= 1; sh++; } if (x_sz != 0) { u32 temp = x & 1; rv |= temp << sh; x_sz >>= 1; x >>= 1; sh++; } } return rv; } static OnLoad _([]() { constexpr u32 x_sz = 1024; for (u32 s = 0; s < 11; s++) { u32 y_sz = 1 << s; for (u32 i = 0; i < x_sz; i++) { detwiddle[0][s][i] = twiddle_slow(i, 0, x_sz, y_sz); detwiddle[1][s][i] = twiddle_slow(0, i, y_sz, x_sz); } } }); void palette_update() { if (!pal_needs_update) return; pal_needs_update = false; rend_updatePalette(); if (!isDirectX(config::RendererType)) { switch (PAL_RAM_CTRL & 3) { case 0: for (int i = 0; i < 1024; i++) { palette16_ram[i] = Unpacker1555::unpack(PALETTE_RAM[i]); palette32_ram[i] = Unpacker1555_32::unpack(PALETTE_RAM[i]); } break; case 1: for (int i = 0; i < 1024; i++) { palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); palette32_ram[i] = Unpacker565_32::unpack(PALETTE_RAM[i]); } break; case 2: for (int i = 0; i < 1024; i++) { palette16_ram[i] = Unpacker4444::unpack(PALETTE_RAM[i]); palette32_ram[i] = Unpacker4444_32::unpack(PALETTE_RAM[i]); } break; case 3: for (int i = 0; i < 1024; i++) palette32_ram[i] = Unpacker8888::unpack(PALETTE_RAM[i]); break; } } else { switch (PAL_RAM_CTRL & 3) { case 0: for (int i = 0; i < 1024; i++) { palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); palette32_ram[i] = Unpacker1555_32::unpack(PALETTE_RAM[i]); } break; case 1: for (int i = 0; i < 1024; i++) { palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); palette32_ram[i] = Unpacker565_32::unpack(PALETTE_RAM[i]); } break; case 2: for (int i = 0; i < 1024; i++) { palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); palette32_ram[i] = Unpacker4444_32::unpack(PALETTE_RAM[i]); } break; case 3: for (int i = 0; i < 1024; i++) palette32_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); break; } } for (std::size_t i = 0; i < std::size(pal_hash_16); i++) pal_hash_16[i] = XXH32(&PALETTE_RAM[i << 4], 16 * 4, 7); for (std::size_t i = 0; i < std::size(pal_hash_256); i++) pal_hash_256[i] = XXH32(&PALETTE_RAM[i << 8], 256 * 4, 7); } template inline static u32 YUV422(s32 Y, s32 Yu, s32 Yv) { Yu -= 128; Yv -= 128; s32 R = Y + Yv * 11 / 8; // Y + (Yv-128) * (11/8) ? s32 G = Y - (Yu * 11 + Yv * 22) / 32; // Y - (Yu-128) * (11/8) * 0.25 - (Yv-128) * (11/8) * 0.5 ? s32 B = Y + Yu * 110 / 64; // Y + (Yu-128) * (11/8) * 1.25 ? return Packer::pack(std::clamp(R, 0, 255), std::clamp(G, 0, 255), std::clamp(B, 0, 255), 0xFF); } static u32 twop(u32 x, u32 y, u32 bcx, u32 bcy) { return detwiddle[0][bcy][x] + detwiddle[1][bcx][y]; } template struct ConvertPlanar { using unpacked_type = typename Unpacker::unpacked_type; static constexpr u32 xpp = 4; static constexpr u32 ypp = 1; static void Convert(PixelBuffer *pb, const u8 *data) { const u16 *p_in = (const u16 *)data; pb->prel(0, Unpacker::unpack(p_in[0])); pb->prel(1, Unpacker::unpack(p_in[1])); pb->prel(2, Unpacker::unpack(p_in[2])); pb->prel(3, Unpacker::unpack(p_in[3])); } }; template struct ConvertPlanarYUV { using unpacked_type = u32; static constexpr u32 xpp = 4; static constexpr u32 ypp = 1; static void Convert(PixelBuffer *pb, const u8 *data) { //convert 4x1 4444 to 4x1 8888 const u32 *p_in = (const u32 *)data; s32 Y0 = (p_in[0] >> 8) & 255; // s32 Yu = (p_in[0] >> 0) & 255; //p_in[0] s32 Y1 = (p_in[0] >> 24) & 255; //p_in[3] s32 Yv = (p_in[0] >> 16) & 255; //p_in[2] //0,0 pb->prel(0, YUV422(Y0, Yu, Yv)); //1,0 pb->prel(1, YUV422(Y1, Yu, Yv)); //next 4 bytes p_in += 1; Y0 = (p_in[0] >> 8) & 255; // Yu = (p_in[0] >> 0) & 255; //p_in[0] Y1 = (p_in[0] >> 24) & 255; //p_in[3] Yv = (p_in[0] >> 16) & 255; //p_in[2] //0,0 pb->prel(2, YUV422(Y0, Yu, Yv)); //1,0 pb->prel(3, YUV422(Y1, Yu, Yv)); } }; template struct ConvertTwiddle { using unpacked_type = typename Unpacker::unpacked_type; static constexpr u32 xpp = 2; static constexpr u32 ypp = 2; static void Convert(PixelBuffer *pb, const u8 *data) { const u16 *p_in = (const u16 *)data; pb->prel(0, 0, Unpacker::unpack(p_in[0])); pb->prel(0, 1, Unpacker::unpack(p_in[1])); pb->prel(1, 0, Unpacker::unpack(p_in[2])); pb->prel(1, 1, Unpacker::unpack(p_in[3])); } }; template struct ConvertTwiddleYUV { using unpacked_type = u32; static constexpr u32 xpp = 2; static constexpr u32 ypp = 2; static void Convert(PixelBuffer *pb, const u8 *data) { //convert 4x1 4444 to 4x1 8888 const u16* p_in = (const u16 *)data; s32 Y0 = (p_in[0] >> 8) & 255; // s32 Yu = (p_in[0] >> 0) & 255; //p_in[0] s32 Y1 = (p_in[2] >> 8) & 255; //p_in[3] s32 Yv = (p_in[2] >> 0) & 255; //p_in[2] //0,0 pb->prel(0, 0, YUV422(Y0, Yu, Yv)); //1,0 pb->prel(1, 0, YUV422(Y1, Yu, Yv)); //next 4 bytes //p_in+=2; Y0 = (p_in[1] >> 8) & 255; // Yu = (p_in[1] >> 0) & 255; //p_in[0] Y1 = (p_in[3] >> 8) & 255; //p_in[3] Yv = (p_in[3] >> 0) & 255; //p_in[2] //0,1 pb->prel(0, 1, YUV422(Y0, Yu, Yv)); //1,1 pb->prel(1, 1, YUV422(Y1, Yu, Yv)); } }; template struct UnpackerPalToRgb { using unpacked_type = Pixel; static Pixel unpack(u8 col) { u32 *pal = sizeof(Pixel) == 2 ? &palette16_ram[palette_index] : &palette32_ram[palette_index]; return pal[col]; } }; template struct ConvertTwiddlePal4 { using unpacked_type = typename Unpacker::unpacked_type; static constexpr u32 xpp = 4; static constexpr u32 ypp = 4; static void Convert(PixelBuffer *pb, const u8 *data) { const u8 *p_in = data; pb->prel(0, 0, Unpacker::unpack(p_in[0] & 0xF)); pb->prel(0, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; pb->prel(1, 0, Unpacker::unpack(p_in[0] & 0xF)); pb->prel(1, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; pb->prel(0, 2, Unpacker::unpack(p_in[0] & 0xF)); pb->prel(0, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; pb->prel(1, 2, Unpacker::unpack(p_in[0] & 0xF)); pb->prel(1, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; pb->prel(2, 0, Unpacker::unpack(p_in[0] & 0xF)); pb->prel(2, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; pb->prel(3, 0, Unpacker::unpack(p_in[0] & 0xF)); pb->prel(3, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; pb->prel(2, 2, Unpacker::unpack(p_in[0] & 0xF)); pb->prel(2, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; pb->prel(3, 2, Unpacker::unpack(p_in[0] & 0xF)); pb->prel(3, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; } }; template struct ConvertTwiddlePal8 { using unpacked_type = typename Unpacker::unpacked_type; static constexpr u32 xpp = 2; static constexpr u32 ypp = 4; static void Convert(PixelBuffer *pb, const u8 *data) { const u8* p_in = (const u8 *)data; pb->prel(0, 0, Unpacker::unpack(p_in[0])); p_in++; pb->prel(0, 1, Unpacker::unpack(p_in[0])); p_in++; pb->prel(1, 0, Unpacker::unpack(p_in[0])); p_in++; pb->prel(1, 1, Unpacker::unpack(p_in[0])); p_in++; pb->prel(0, 2, Unpacker::unpack(p_in[0])); p_in++; pb->prel(0, 3, Unpacker::unpack(p_in[0])); p_in++; pb->prel(1, 2, Unpacker::unpack(p_in[0])); p_in++; pb->prel(1, 3, Unpacker::unpack(p_in[0])); p_in++; } }; //handler functions template void texture_PL(PixelBuffer* pb, const u8* p_in, u32 width, u32 height) { pb->amove(0,0); height /= PixelConvertor::ypp; width /= PixelConvertor::xpp; for (u32 y = 0; y < height; y++) { for (u32 x = 0; x < width; x++) { const u8* p = p_in; PixelConvertor::Convert(pb, p); p_in += 8; pb->rmovex(PixelConvertor::xpp); } pb->rmovey(PixelConvertor::ypp); } } template void texture_PLVQ(PixelBuffer* pb, const u8* p_in, u32 width, u32 height) { pb->amove(0, 0); height /= PixelConvertor::ypp; width /= PixelConvertor::xpp; for (u32 y = 0; y < height; y++) { for (u32 x = 0; x < width; x++) { u8 p = *p_in++; PixelConvertor::Convert(pb, &vq_codebook[p * 8]); pb->rmovex(PixelConvertor::xpp); } pb->rmovey(PixelConvertor::ypp); } } template void texture_TW(PixelBuffer* pb, const u8* p_in, u32 width, u32 height) { pb->amove(0, 0); const u32 divider = PixelConvertor::xpp * PixelConvertor::ypp; const u32 bcx = bitscanrev(width); const u32 bcy = bitscanrev(height); for (u32 y = 0; y < height; y += PixelConvertor::ypp) { for (u32 x = 0; x < width; x += PixelConvertor::xpp) { const u8* p = &p_in[(twop(x, y, bcx, bcy) / divider) << 3]; PixelConvertor::Convert(pb, p); pb->rmovex(PixelConvertor::xpp); } pb->rmovey(PixelConvertor::ypp); } } template void texture_VQ(PixelBuffer* pb, const u8* p_in, u32 width, u32 height) { pb->amove(0, 0); const u32 divider = PixelConvertor::xpp * PixelConvertor::ypp; const u32 bcx = bitscanrev(width); const u32 bcy = bitscanrev(height); for (u32 y = 0; y < height; y += PixelConvertor::ypp) { for (u32 x = 0; x < width; x += PixelConvertor::xpp) { u8 p = p_in[twop(x, y, bcx, bcy) / divider]; PixelConvertor::Convert(pb, &vq_codebook[p * 8]); pb->rmovex(PixelConvertor::xpp); } pb->rmovey(PixelConvertor::ypp); } } //Twiddle const TexConvFP tex565_TW = texture_TW>>; // Palette const TexConvFP texPAL4_TW = texture_TW>>; const TexConvFP texPAL8_TW = texture_TW>>; const TexConvFP32 texPAL4_TW32 = texture_TW>>; const TexConvFP32 texPAL8_TW32 = texture_TW>>; const TexConvFP8 texPAL4PT_TW = texture_TW>>; const TexConvFP8 texPAL8PT_TW = texture_TW>>; //VQ const TexConvFP tex565_VQ = texture_VQ>>; // According to the documentation, a texture cannot be compressed and use // a palette at the same time. However the hardware displays them // just fine. const TexConvFP texPAL4_VQ = texture_VQ>>; const TexConvFP texPAL8_VQ = texture_VQ>>; const TexConvFP32 texPAL4_VQ32 = texture_VQ>>; const TexConvFP32 texPAL8_VQ32 = texture_VQ>>; namespace opengl { // OpenGL //Planar const TexConvFP32 texYUV422_PL = texture_PL>; const TexConvFP32 tex565_PL32 = texture_PL>>; const TexConvFP32 tex1555_PL32 = texture_PL>>; const TexConvFP32 tex4444_PL32 = texture_PL>>; const TexConvFP32 texYUV422_PLVQ = texture_PLVQ>; const TexConvFP32 tex565_PLVQ32 = texture_PLVQ>>; const TexConvFP32 tex1555_PLVQ32 = texture_PLVQ>>; const TexConvFP32 tex4444_PLVQ32 = texture_PLVQ>>; //Twiddle const TexConvFP tex1555_TW = texture_TW>; const TexConvFP tex4444_TW = texture_TW>; const TexConvFP texBMP_TW = tex4444_TW; const TexConvFP32 texYUV422_TW = texture_TW>; const TexConvFP32 tex565_TW32 = texture_TW>>; const TexConvFP32 tex1555_TW32 = texture_TW>>; const TexConvFP32 tex4444_TW32 = texture_TW>>; //VQ const TexConvFP tex1555_VQ = texture_VQ>; const TexConvFP tex4444_VQ = texture_VQ>; const TexConvFP texBMP_VQ = tex4444_VQ; const TexConvFP32 texYUV422_VQ = texture_VQ>; const TexConvFP32 tex565_VQ32 = texture_VQ>>; const TexConvFP32 tex1555_VQ32 = texture_VQ>>; const TexConvFP32 tex4444_VQ32 = texture_VQ>>; } namespace directx { // DirectX //Planar const TexConvFP32 texYUV422_PL = texture_PL>; const TexConvFP32 tex565_PL32 = texture_PL>>; const TexConvFP32 tex1555_PL32 = texture_PL>>; const TexConvFP32 tex4444_PL32 = texture_PL>>; const TexConvFP32 texYUV422_PLVQ = texture_PLVQ>; const TexConvFP32 tex565_PLVQ32 = texture_PLVQ>>; const TexConvFP32 tex1555_PLVQ32 = texture_PLVQ>>; const TexConvFP32 tex4444_PLVQ32 = texture_PLVQ>>; //Twiddle const TexConvFP tex1555_TW = texture_TW>>; const TexConvFP tex4444_TW = texture_TW>>; const TexConvFP texBMP_TW = tex4444_TW; const TexConvFP32 texYUV422_TW = texture_TW>; const TexConvFP32 tex565_TW32 = texture_TW>>; const TexConvFP32 tex1555_TW32 = texture_TW>>; const TexConvFP32 tex4444_TW32 = texture_TW>>; //VQ const TexConvFP tex1555_VQ = texture_VQ>>; const TexConvFP tex4444_VQ = texture_VQ>>; const TexConvFP texBMP_VQ = tex4444_VQ; const TexConvFP32 texYUV422_VQ = texture_VQ>; const TexConvFP32 tex565_VQ32 = texture_VQ>>; const TexConvFP32 tex1555_VQ32 = texture_VQ>>; const TexConvFP32 tex4444_VQ32 = texture_VQ>>; } #define TEX_CONV_TABLE \ const PvrTexInfo pvrTexInfo[8] = \ { /* name bpp Final format Twiddled VQ Planar(32b) Twiddled(32b) VQ (32b) PL VQ (32b) Palette (8b) */ \ {"1555", 16, TextureType::_5551, tex1555_TW, tex1555_VQ, tex1555_PL32, tex1555_TW32, tex1555_VQ32, tex1555_PLVQ32, nullptr }, \ {"565", 16, TextureType::_565, tex565_TW, tex565_VQ, tex565_PL32, tex565_TW32, tex565_VQ32, tex565_PLVQ32, nullptr }, \ {"4444", 16, TextureType::_4444, tex4444_TW, tex4444_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, tex4444_PLVQ32, nullptr }, \ {"yuv", 16, TextureType::_8888, nullptr, nullptr, texYUV422_PL, texYUV422_TW, texYUV422_VQ, texYUV422_PLVQ, nullptr }, \ {"bumpmap", 16, TextureType::_4444, texBMP_TW, texBMP_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, tex4444_PLVQ32, nullptr }, \ {"pal4", 4, TextureType::_5551, texPAL4_TW, texPAL4_VQ, nullptr, texPAL4_TW32, texPAL4_VQ32, nullptr, texPAL4PT_TW }, \ {"pal8", 8, TextureType::_5551, texPAL8_TW, texPAL8_VQ, nullptr, texPAL8_TW32, texPAL8_VQ32, nullptr, texPAL8PT_TW }, \ {"ns/1555", 0}, \ } namespace opengl { TEX_CONV_TABLE; } namespace directx { TEX_CONV_TABLE; } #undef TEX_CONV_TABLE const PvrTexInfo *pvrTexInfo = opengl::pvrTexInfo;