// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team // SPDX-License-Identifier: GPL-3.0+ #include "GSLocalMemory.h" #include "GS.h" #include "GSBlock.h" #include "GSExtra.h" class CURRENT_ISA::GSLocalMemoryFunctions { template static void WriteImageColumn(GSLocalMemory& mem, int l, int r, int y, int h, const u8* src, int srcpitch, const GIFRegBITBLTBUF& BITBLTBUF); template static void WriteImageBlock(GSLocalMemory& mem, int l, int r, int y, int h, const u8* src, int srcpitch, const GIFRegBITBLTBUF& BITBLTBUF); template static void WriteImageLeftRight(GSLocalMemory& mem, int l, int r, int y, int h, const u8* src, int srcpitch, const GIFRegBITBLTBUF& BITBLTBUF); template static void WriteImageTopBottom(GSLocalMemory& mem, int l, int r, int y, int h, const u8* src, int srcpitch, const GIFRegBITBLTBUF& BITBLTBUF); template static void WriteImage(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG); static void WriteImage24(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG); static void WriteImage8H(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG); static void WriteImage4HL(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG); static void WriteImage4HH(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG); static void WriteImage24Z(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG); static void WriteImageX(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG); // TODO: ReadImage32/24/... static void ReadTexture32(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureGPU24(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture24(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture16(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture8(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture4(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture8H(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture4HL(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture4HH(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock32(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock24(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock16(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock8(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock4(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock8H(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock4HL(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock4HH(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture8P(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture4P(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture8HP(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture4HLP(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture4HHP(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock8P(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock4P(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock8HP(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock4HLP(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock4HHP(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); #if _M_SSE == 0x501 static void ReadTexture8HSW(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTexture8HHSW(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock8HSW(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); static void ReadTextureBlock8HHSW(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); #endif template static void ReadTexture(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA); public: static void ReadImageX(const GSLocalMemory& mem, int& tx, int& ty, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG); static void PopulateFunctions(GSLocalMemory& mem); }; MULTI_ISA_UNSHARED_IMPL; void CURRENT_ISA::GSLocalMemoryPopulateFunctions(GSLocalMemory& mem) { GSLocalMemoryFunctions::PopulateFunctions(mem); } void GSLocalMemoryFunctions::PopulateFunctions(GSLocalMemory& mem) { mem.m_readImageX = ReadImageX; for (GSLocalMemory::psm_t& psm : mem.m_psm) { psm.wi = WriteImage; psm.ri = ReadImageX; // TODO psm.rtx = ReadTexture32; psm.rtxP = ReadTexture32; psm.rtxb = ReadTextureBlock32; psm.rtxbP = ReadTextureBlock32; } mem.m_psm[PSMCT24].wi = WriteImage24; // TODO mem.m_psm[PSMCT16].wi = WriteImage; mem.m_psm[PSMCT16S].wi = WriteImage; mem.m_psm[PSMT8].wi = WriteImage; mem.m_psm[PSMT4].wi = WriteImage; mem.m_psm[PSMT8H].wi = WriteImage8H; // TODO mem.m_psm[PSMT4HL].wi = WriteImage4HL; // TODO mem.m_psm[PSMT4HH].wi = WriteImage4HH; // TODO mem.m_psm[PSMZ32].wi = WriteImage; mem.m_psm[PSMZ24].wi = WriteImage24Z; // TODO mem.m_psm[PSMZ16].wi = WriteImage; mem.m_psm[PSMZ16S].wi = WriteImage; mem.m_psm[PSMCT24].rtx = ReadTexture24; mem.m_psm[PSGPU24].rtx = ReadTextureGPU24; mem.m_psm[PSMCT16].rtx = ReadTexture16; mem.m_psm[PSMCT16S].rtx = ReadTexture16; mem.m_psm[PSMT8].rtx = ReadTexture8; mem.m_psm[PSMT4].rtx = ReadTexture4; mem.m_psm[PSMT8H].rtx = ReadTexture8H; mem.m_psm[PSMT4HL].rtx = ReadTexture4HL; mem.m_psm[PSMT4HH].rtx = ReadTexture4HH; mem.m_psm[PSMZ32].rtx = ReadTexture32; mem.m_psm[PSMZ24].rtx = ReadTexture24; mem.m_psm[PSMZ16].rtx = ReadTexture16; mem.m_psm[PSMZ16S].rtx = ReadTexture16; mem.m_psm[PSMCT24].rtxP = ReadTexture24; mem.m_psm[PSMCT16].rtxP = ReadTexture16; mem.m_psm[PSMCT16S].rtxP = ReadTexture16; mem.m_psm[PSMT8].rtxP = ReadTexture8P; mem.m_psm[PSMT4].rtxP = ReadTexture4P; mem.m_psm[PSMT8H].rtxP = ReadTexture8HP; mem.m_psm[PSMT4HL].rtxP = ReadTexture4HLP; mem.m_psm[PSMT4HH].rtxP = ReadTexture4HHP; mem.m_psm[PSMZ32].rtxP = ReadTexture32; mem.m_psm[PSMZ24].rtxP = ReadTexture24; mem.m_psm[PSMZ16].rtxP = ReadTexture16; mem.m_psm[PSMZ16S].rtxP = ReadTexture16; mem.m_psm[PSMCT24].rtxb = ReadTextureBlock24; mem.m_psm[PSMCT16].rtxb = ReadTextureBlock16; mem.m_psm[PSMCT16S].rtxb = ReadTextureBlock16; mem.m_psm[PSMT8].rtxb = ReadTextureBlock8; mem.m_psm[PSMT4].rtxb = ReadTextureBlock4; mem.m_psm[PSMT8H].rtxb = ReadTextureBlock8H; mem.m_psm[PSMT4HL].rtxb = ReadTextureBlock4HL; mem.m_psm[PSMT4HH].rtxb = ReadTextureBlock4HH; mem.m_psm[PSMZ32].rtxb = ReadTextureBlock32; mem.m_psm[PSMZ24].rtxb = ReadTextureBlock24; mem.m_psm[PSMZ16].rtxb = ReadTextureBlock16; mem.m_psm[PSMZ16S].rtxb = ReadTextureBlock16; mem.m_psm[PSMCT24].rtxbP = ReadTextureBlock24; mem.m_psm[PSMCT16].rtxbP = ReadTextureBlock16; mem.m_psm[PSMCT16S].rtxbP = ReadTextureBlock16; mem.m_psm[PSMT8].rtxbP = ReadTextureBlock8P; mem.m_psm[PSMT4].rtxbP = ReadTextureBlock4P; mem.m_psm[PSMT8H].rtxbP = ReadTextureBlock8HP; mem.m_psm[PSMT4HL].rtxbP = ReadTextureBlock4HLP; mem.m_psm[PSMT4HH].rtxbP = ReadTextureBlock4HHP; mem.m_psm[PSMZ32].rtxbP = ReadTextureBlock32; mem.m_psm[PSMZ24].rtxbP = ReadTextureBlock24; mem.m_psm[PSMZ16].rtxbP = ReadTextureBlock16; mem.m_psm[PSMZ16S].rtxbP = ReadTextureBlock16; #if _M_SSE == 0x501 if (g_cpu.hasSlowGather) { mem.m_psm[PSMT8].rtx = ReadTexture8HSW; mem.m_psm[PSMT8H].rtx = ReadTexture8HHSW; mem.m_psm[PSMT8].rtxb = ReadTextureBlock8HSW; mem.m_psm[PSMT8H].rtxb = ReadTextureBlock8HHSW; } #endif } template static void foreachBlock(const GSOffset& off, GSLocalMemory& mem, const GSVector4i& r, u8* dst, int dstpitch, int bpp, Fn&& fn) { pxAssert(off.isBlockAligned(r)); GSOffset::BNHelper bn = off.bnMulti(r.left, r.top); int right = r.right >> off.blockShiftX(); int bottom = r.bottom >> off.blockShiftY(); int offset = dstpitch << off.blockShiftY(); int xAdd = (1 << off.blockShiftX()) * (bpp / 8); for (; bn.blkY() < bottom; bn.nextBlockY(), dst += offset) { for (int x = 0; bn.blkX() < right; bn.nextBlockX(), x += xAdd) { const u8* src = mem.BlockPtr(bn.value()); u8* read_dst = dst + x; fn(read_dst, src); } } } template void GSLocalMemoryFunctions::WriteImageColumn(GSLocalMemory& mem, int l, int r, int y, int h, const u8* src, int srcpitch, const GIFRegBITBLTBUF& BITBLTBUF) { u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; const int csy = bsy / 4; for (int offset = srcpitch * csy; h >= csy; h -= csy, y += csy, src += offset) { for (int x = l; x < r; x += bsx) { switch (psm) { case PSMCT32: GSBlock::WriteColumn32(y, mem.BlockPtr32(x, y, bp, bw), &src[x * 4], srcpitch); break; case PSMCT16: GSBlock::WriteColumn16(y, mem.BlockPtr16(x, y, bp, bw), &src[x * 2], srcpitch); break; case PSMCT16S: GSBlock::WriteColumn16(y, mem.BlockPtr16S(x, y, bp, bw), &src[x * 2], srcpitch); break; case PSMT8: GSBlock::WriteColumn8(y, mem.BlockPtr8(x, y, bp, bw), &src[x], srcpitch); break; case PSMT4: GSBlock::WriteColumn4(y, mem.BlockPtr4(x, y, bp, bw), &src[x >> 1], srcpitch); break; case PSMZ32: GSBlock::WriteColumn32(y, mem.BlockPtr32Z(x, y, bp, bw), &src[x * 4], srcpitch); break; case PSMZ16: GSBlock::WriteColumn16(y, mem.BlockPtr16Z(x, y, bp, bw), &src[x * 2], srcpitch); break; case PSMZ16S: GSBlock::WriteColumn16(y, mem.BlockPtr16SZ(x, y, bp, bw), &src[x * 2], srcpitch); break; // TODO default: ASSUME(0); } } } } template void GSLocalMemoryFunctions::WriteImageBlock(GSLocalMemory& mem, int l, int r, int y, int h, const u8* src, int srcpitch, const GIFRegBITBLTBUF& BITBLTBUF) { u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; for (int offset = srcpitch * bsy; h >= bsy; h -= bsy, y += bsy, src += offset) { for (int x = l; x < r; x += bsx) { switch (psm) { case PSMCT32: GSBlock::WriteBlock32(mem.BlockPtr32(x, y, bp, bw), &src[x * 4], srcpitch); break; case PSMCT16: GSBlock::WriteBlock16(mem.BlockPtr16(x, y, bp, bw), &src[x * 2], srcpitch); break; case PSMCT16S: GSBlock::WriteBlock16(mem.BlockPtr16S(x, y, bp, bw), &src[x * 2], srcpitch); break; case PSMT8: GSBlock::WriteBlock8(mem.BlockPtr8(x, y, bp, bw), &src[x], srcpitch); break; case PSMT4: GSBlock::WriteBlock4(mem.BlockPtr4(x, y, bp, bw), &src[x >> 1], srcpitch); break; case PSMZ32: GSBlock::WriteBlock32(mem.BlockPtr32Z(x, y, bp, bw), &src[x * 4], srcpitch); break; case PSMZ16: GSBlock::WriteBlock16(mem.BlockPtr16Z(x, y, bp, bw), &src[x * 2], srcpitch); break; case PSMZ16S: GSBlock::WriteBlock16(mem.BlockPtr16SZ(x, y, bp, bw), &src[x * 2], srcpitch); break; // TODO default: ASSUME(0); } } } } template void GSLocalMemoryFunctions::WriteImageLeftRight(GSLocalMemory& mem, int l, int r, int y, int h, const u8* src, int srcpitch, const GIFRegBITBLTBUF& BITBLTBUF) { u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; for (; h > 0; y++, h--, src += srcpitch) { for (int x = l; x < r; x++) { switch (psm) { case PSMCT32: mem.WritePixel32(x, y, *(u32*)&src[x * 4], bp, bw); break; case PSMCT16: mem.WritePixel16(x, y, *(u16*)&src[x * 2], bp, bw); break; case PSMCT16S: mem.WritePixel16S(x, y, *(u16*)&src[x * 2], bp, bw); break; case PSMT8: mem.WritePixel8(x, y, src[x], bp, bw); break; case PSMT4: mem.WritePixel4(x, y, src[x >> 1] >> ((x & 1) << 2), bp, bw); break; case PSMZ32: mem.WritePixel32Z(x, y, *(u32*)&src[x * 4], bp, bw); break; case PSMZ16: mem.WritePixel16Z(x, y, *(u16*)&src[x * 2], bp, bw); break; case PSMZ16S: mem.WritePixel16SZ(x, y, *(u16*)&src[x * 2], bp, bw); break; // TODO default: ASSUME(0); } } } } template void GSLocalMemoryFunctions::WriteImageTopBottom(GSLocalMemory& mem, int l, int r, int y, int h, const u8* src, int srcpitch, const GIFRegBITBLTBUF& BITBLTBUF) { alignas(32) u8 buff[64]; // merge buffer for one column u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; const int csy = bsy / 4; // merge incomplete column int y2 = y & (csy - 1); if (y2 > 0) { int h2 = std::min(h, csy - y2); for (int x = l; x < r; x += bsx) { u8* dst = NULL; switch (psm) { case PSMCT32: dst = mem.BlockPtr32(x, y, bp, bw); break; case PSMCT16: dst = mem.BlockPtr16(x, y, bp, bw); break; case PSMCT16S: dst = mem.BlockPtr16S(x, y, bp, bw); break; case PSMT8: dst = mem.BlockPtr8(x, y, bp, bw); break; case PSMT4: dst = mem.BlockPtr4(x, y, bp, bw); break; case PSMZ32: dst = mem.BlockPtr32Z(x, y, bp, bw); break; case PSMZ16: dst = mem.BlockPtr16Z(x, y, bp, bw); break; case PSMZ16S: dst = mem.BlockPtr16SZ(x, y, bp, bw); break; // TODO default: ASSUME(0); } switch (psm) { case PSMCT32: case PSMZ32: GSBlock::ReadColumn32(y, dst, buff, 32); memcpy(&buff[32], &src[x * 4], 32); GSBlock::WriteColumn32<32, 0xffffffff>(y, dst, buff, 32); break; case PSMCT16: case PSMCT16S: case PSMZ16: case PSMZ16S: GSBlock::ReadColumn16(y, dst, buff, 32); memcpy(&buff[32], &src[x * 2], 32); GSBlock::WriteColumn16<32>(y, dst, buff, 32); break; case PSMT8: GSBlock::ReadColumn8(y, dst, buff, 16); for (int i = 0, j = y2; i < h2; i++, j++) memcpy(&buff[j * 16], &src[i * srcpitch + x], 16); GSBlock::WriteColumn8<32>(y, dst, buff, 16); break; case PSMT4: GSBlock::ReadColumn4(y, dst, buff, 16); for (int i = 0, j = y2; i < h2; i++, j++) memcpy(&buff[j * 16], &src[i * srcpitch + (x >> 1)], 16); GSBlock::WriteColumn4<32>(y, dst, buff, 16); break; // TODO default: ASSUME(0); } } src += srcpitch * h2; y += h2; h -= h2; } // write whole columns { int h2 = h & ~(csy - 1); if (h2 > 0) { #if FAST_UNALIGNED WriteImageColumn(mem, l, r, y, h2, src, srcpitch, BITBLTBUF); #else size_t addr = (size_t)&src[l * trbpp >> 3]; if ((addr & 31) == 0 && (srcpitch & 31) == 0) { WriteImageColumn(mem, l, r, y, h2, src, srcpitch, BITBLTBUF); } else if ((addr & 15) == 0 && (srcpitch & 15) == 0) { WriteImageColumn(mem, l, r, y, h2, src, srcpitch, BITBLTBUF); } else { WriteImageColumn(mem, l, r, y, h2, src, srcpitch, BITBLTBUF); } #endif src += srcpitch * h2; y += h2; h -= h2; } } // merge incomplete column if (h >= 1) { for (int x = l; x < r; x += bsx) { u8* dst = NULL; switch (psm) { case PSMCT32: dst = mem.BlockPtr32(x, y, bp, bw); break; case PSMCT16: dst = mem.BlockPtr16(x, y, bp, bw); break; case PSMCT16S: dst = mem.BlockPtr16S(x, y, bp, bw); break; case PSMT8: dst = mem.BlockPtr8(x, y, bp, bw); break; case PSMT4: dst = mem.BlockPtr4(x, y, bp, bw); break; case PSMZ32: dst = mem.BlockPtr32Z(x, y, bp, bw); break; case PSMZ16: dst = mem.BlockPtr16Z(x, y, bp, bw); break; case PSMZ16S: dst = mem.BlockPtr16SZ(x, y, bp, bw); break; // TODO default: ASSUME(0); } switch (psm) { case PSMCT32: case PSMZ32: GSBlock::ReadColumn32(y, dst, buff, 32); memcpy(&buff[0], &src[x * 4], 32); GSBlock::WriteColumn32<32, 0xffffffff>(y, dst, buff, 32); break; case PSMCT16: case PSMCT16S: case PSMZ16: case PSMZ16S: GSBlock::ReadColumn16(y, dst, buff, 32); memcpy(&buff[0], &src[x * 2], 32); GSBlock::WriteColumn16<32>(y, dst, buff, 32); break; case PSMT8: GSBlock::ReadColumn8(y, dst, buff, 16); for (int i = 0; i < h; i++) memcpy(&buff[i * 16], &src[i * srcpitch + x], 16); GSBlock::WriteColumn8<32>(y, dst, buff, 16); break; case PSMT4: GSBlock::ReadColumn4(y, dst, buff, 16); for (int i = 0; i < h; i++) memcpy(&buff[i * 16], &src[i * srcpitch + (x >> 1)], 16); GSBlock::WriteColumn4<32>(y, dst, buff, 16); break; // TODO default: ASSUME(0); } } } } template void GSLocalMemoryFunctions::WriteImage(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) { if (TRXREG.RRW == 0) return; const int l = (int)TRXPOS.DSAX; const int r = l + (int)TRXREG.RRW; // finish the incomplete row first if (tx != l) { int n = std::min(len, (r - tx) * trbpp >> 3); WriteImageX(mem, tx, ty, src, n, BITBLTBUF, TRXPOS, TRXREG); src += n; len -= n; } const int la = (l + (bsx - 1)) & ~(bsx - 1); const int ra = r & ~(bsx - 1); // Round up to the nearest byte (NFL 2K5 does r = 1, l = 0 bpp =4, causing divide by zero) const int srcpitch = (((r - l) * trbpp) + 7) >> 3; int h = len / srcpitch; // Slow path for odd width 4bpp, the fast path expects everything to be perfectly aligned and great, // but things get hairy with 4bpp pixels and odd widths since the lowest size we can address is 8bits, it goes out of sync. // Although I call this a slow path, it's probably faster than modifying the data alignment every other line. // GT3 demo, Jak 2 Japanese subtitles, and the BG Dark Alliance minimap do this. if (trbpp == 4 && (TRXREG.RRW & 0x1)) { int count = 0; const int t = TRXPOS.DSAY; const int b = t + (int)TRXREG.RRH; for (int y = t; y < b; y++) { for (int x = l; x < r; x++) { mem.WritePixel4(x, y, src[count >> 1] >> ((count & 1) << 2), BITBLTBUF.DBP, BITBLTBUF.DBW); count++; } } return; } if (ra - la >= bsx && h > 0) // "transfer width" >= "block width" && there is at least one full row { const u8* s = &src[-l * trbpp >> 3]; src += srcpitch * h; len -= srcpitch * h; // left part if (l < la) { WriteImageLeftRight(mem, l, la, ty, h, s, srcpitch, BITBLTBUF); } // right part if (ra < r) { WriteImageLeftRight(mem, ra, r, ty, h, s, srcpitch, BITBLTBUF); } // horizontally aligned part if (la < ra) { // top part { int h2 = std::min(h, bsy - (ty & (bsy - 1))); if (h2 < bsy) { WriteImageTopBottom(mem, la, ra, ty, h2, s, srcpitch, BITBLTBUF); s += srcpitch * h2; ty += h2; h -= h2; } } // horizontally and vertically aligned part { int h2 = h & ~(bsy - 1); if (h2 > 0) { #if FAST_UNALIGNED WriteImageBlock(mem, la, ra, ty, h2, s, srcpitch, BITBLTBUF); #else size_t addr = (size_t)&s[la * trbpp >> 3]; if ((addr & 31) == 0 && (srcpitch & 31) == 0) { WriteImageBlock(mem, la, ra, ty, h2, s, srcpitch, BITBLTBUF); } else if ((addr & 15) == 0 && (srcpitch & 15) == 0) { WriteImageBlock(mem, la, ra, ty, h2, s, srcpitch, BITBLTBUF); } else { WriteImageBlock(mem, la, ra, ty, h2, s, srcpitch, BITBLTBUF); } #endif s += srcpitch * h2; ty += h2; h -= h2; } } // bottom part if (h > 0) { WriteImageTopBottom(mem, la, ra, ty, h, s, srcpitch, BITBLTBUF); // s += srcpitch * h; ty += h; // h -= h; } } } // the rest if (len > 0) { WriteImageX(mem, tx, ty, src, len, BITBLTBUF, TRXPOS, TRXREG); } } static bool IsTopLeftAligned(int dsax, int tx, int ty, int bw, int bh) { return ((dsax & (bw - 1)) == 0 && (tx & (bw - 1)) == 0 && dsax == tx && (ty & (bh - 1)) == 0); } void GSLocalMemoryFunctions::WriteImage24(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) { if (TRXREG.RRW == 0) return; u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; int tw = TRXPOS.DSAX + TRXREG.RRW, srcpitch = TRXREG.RRW * 3; int th = len / srcpitch; bool aligned = IsTopLeftAligned(TRXPOS.DSAX, tx, ty, 8, 8); if (!aligned || (tw & 7) || (th & 7) || (len % srcpitch)) { // TODO WriteImageX(mem, tx, ty, src, len, BITBLTBUF, TRXPOS, TRXREG); } else { th += ty; for (int y = ty; y < th; y += 8, src += srcpitch * 8) { for (int x = tx; x < tw; x += 8) { GSBlock::UnpackAndWriteBlock24(src + (x - tx) * 3, srcpitch, mem.BlockPtr32(x, y, bp, bw)); } } ty = th; } } void GSLocalMemoryFunctions::WriteImage8H(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) { if (TRXREG.RRW == 0) return; u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; int tw = TRXPOS.DSAX + TRXREG.RRW, srcpitch = TRXREG.RRW; int th = len / srcpitch; bool aligned = IsTopLeftAligned(TRXPOS.DSAX, tx, ty, 8, 8); if (!aligned || (tw & 7) || (th & 7) || (len % srcpitch)) { // TODO WriteImageX(mem, tx, ty, src, len, BITBLTBUF, TRXPOS, TRXREG); } else { th += ty; for (int y = ty; y < th; y += 8, src += srcpitch * 8) { for (int x = tx; x < tw; x += 8) { GSBlock::UnpackAndWriteBlock8H(src + (x - tx), srcpitch, mem.BlockPtr32(x, y, bp, bw)); } } ty = th; } } void GSLocalMemoryFunctions::WriteImage4HL(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) { if (TRXREG.RRW == 0) return; u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; int tw = TRXPOS.DSAX + TRXREG.RRW, srcpitch = TRXREG.RRW / 2; int th = len / srcpitch; bool aligned = IsTopLeftAligned(TRXPOS.DSAX, tx, ty, 8, 8); if (!aligned || (tw & 7) || (th & 7) || (len % srcpitch)) { // TODO WriteImageX(mem, tx, ty, src, len, BITBLTBUF, TRXPOS, TRXREG); } else { th += ty; for (int y = ty; y < th; y += 8, src += srcpitch * 8) { for (int x = tx; x < tw; x += 8) { GSBlock::UnpackAndWriteBlock4HL(src + (x - tx) / 2, srcpitch, mem.BlockPtr32(x, y, bp, bw)); } } ty = th; } } void GSLocalMemoryFunctions::WriteImage4HH(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) { if (TRXREG.RRW == 0) return; u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; int tw = TRXPOS.DSAX + TRXREG.RRW, srcpitch = TRXREG.RRW / 2; int th = len / srcpitch; bool aligned = IsTopLeftAligned(TRXPOS.DSAX, tx, ty, 8, 8); if (!aligned || (tw & 7) || (th & 7) || (len % srcpitch)) { // TODO WriteImageX(mem, tx, ty, src, len, BITBLTBUF, TRXPOS, TRXREG); } else { th += ty; for (int y = ty; y < th; y += 8, src += srcpitch * 8) { for (int x = tx; x < tw; x += 8) { GSBlock::UnpackAndWriteBlock4HH(src + (x - tx) / 2, srcpitch, mem.BlockPtr32(x, y, bp, bw)); } } ty = th; } } void GSLocalMemoryFunctions::WriteImage24Z(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) { if (TRXREG.RRW == 0) return; u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; int tw = TRXPOS.DSAX + TRXREG.RRW, srcpitch = TRXREG.RRW * 3; int th = len / srcpitch; bool aligned = IsTopLeftAligned(TRXPOS.DSAX, tx, ty, 8, 8); if (!aligned || (tw & 7) || (th & 7) || (len % srcpitch)) { // TODO WriteImageX(mem, tx, ty, src, len, BITBLTBUF, TRXPOS, TRXREG); } else { th += ty; for (int y = ty; y < th; y += 8, src += srcpitch * 8) { for (int x = tx; x < tw; x += 8) { GSBlock::UnpackAndWriteBlock24(src + (x - tx) * 3, srcpitch, mem.BlockPtr32Z(x, y, bp, bw)); } } ty = th; } } /// Helper for WriteImageX and ReadImageX /// `len` is in pixels, unlike WriteImageX/ReadImageX where it's bytes /// `xinc` is the amount to increment `x` by per iteration /// Creates a GSOffset::PAHelper on a starting (x, y) to get the base address for each line, /// then `fn` on the helper and an x offset once for every `xinc` pixels along that line template static void readWriteHelper(int& tx, int& ty, int len, int xinc, int sx, int w, const GSOffset& off, Fn&& fn) { int y = ty; int ex = sx + w; int remX = ex - tx; pxAssert(remX >= 0); GSOffset::PAHelper pa = off.paMulti(tx, y); while (len > 0) { int stop = std::min(remX, len); len -= stop; remX -= stop; for (int x = 0; x < stop; x += xinc) fn(pa, x); if (remX == 0) { y++; remX = w; pa = off.paMulti(sx, y); } } tx = ex - remX; ty = y; } void GSLocalMemoryFunctions::WriteImageX(GSLocalMemory& mem, int& tx, int& ty, const u8* src, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) { if (len <= 0) return; const u8* pb = (u8*)src; const u16* pw = (u16*)src; const u32* pd = (u32*)src; u8* vm8 = mem.vm8(); u16* vm16 = mem.vm16(); u32* vm32 = mem.vm32(); u32 bp = BITBLTBUF.DBP; u32 bw = BITBLTBUF.DBW; int sx = TRXPOS.DSAX; int w = TRXREG.RRW; GSOffset off = mem.GetOffset(bp, bw, BITBLTBUF.DPSM); switch (BITBLTBUF.DPSM) { case PSMCT32: case PSMZ32: readWriteHelper(tx, ty, len / 4, 1, sx, w, off.assertSizesMatch(GSLocalMemory::swizzle32), [&](GSOffset::PAHelper& pa, int x) { vm32[pa.value(x)] = *pd; pd++; }); break; case PSMCT24: case PSMZ24: readWriteHelper(tx, ty, len / 3, 1, sx, w, off.assertSizesMatch(GSLocalMemory::swizzle32), [&](GSOffset::PAHelper& pa, int x) { mem.WritePixel24(&vm32[pa.value(x)], *(u32*)pb); pb += 3; }); break; case PSMCT16: case PSMCT16S: case PSMZ16: case PSMZ16S: readWriteHelper(tx, ty, len / 2, 1, sx, w, off.assertSizesMatch(GSLocalMemory::swizzle16), [&](GSOffset::PAHelper& pa, int x) { vm16[pa.value(x)] = *pw; pw++; }); break; case PSMT8: readWriteHelper(tx, ty, len, 1, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT8), [&](GSOffset::PAHelper& pa, int x) { vm8[pa.value(x)] = *pb; pb++; }); break; case PSMT4: readWriteHelper(tx, ty, len * 2, 2, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT4), [&](GSOffset::PAHelper& pa, int x) { mem.WritePixel4(pa.value(x), *pb & 0xf); mem.WritePixel4(pa.value(x + 1), *pb >> 4); pb++; }); break; case PSMT8H: readWriteHelper(tx, ty, len, 1, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT8H), [&](GSOffset::PAHelper& pa, int x) { mem.WritePixel8H(&vm32[pa.value(x)], *pb); pb++; }); break; case PSMT4HL: readWriteHelper(tx, ty, len * 2, 2, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT4HL), [&](GSOffset::PAHelper& pa, int x) { mem.WritePixel4HL(&vm32[pa.value(x)], *pb & 0xf); mem.WritePixel4HL(&vm32[pa.value(x + 1)], *pb >> 4); pb++; }); break; case PSMT4HH: readWriteHelper(tx, ty, len * 2, 2, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT4HH), [&](GSOffset::PAHelper& pa, int x) { mem.WritePixel4HH(&vm32[pa.value(x)], *pb & 0xf); mem.WritePixel4HH(&vm32[pa.value(x + 1)], *pb >> 4); pb++; }); break; } } // void GSLocalMemoryFunctions::ReadImageX(const GSLocalMemory& mem, int& tx, int& ty, u8* dst, int len, GIFRegBITBLTBUF& BITBLTBUF, GIFRegTRXPOS& TRXPOS, GIFRegTRXREG& TRXREG) { if (len <= 0) return; u8* RESTRICT pb = (u8*)dst; u16* RESTRICT pw = (u16*)dst; u32* RESTRICT pd = (u32*)dst; const u32* vm32 = mem.vm32(); const u16* vm16 = mem.vm16(); const u8* vm8 = mem.vm8(); u32 bp = BITBLTBUF.SBP; u32 bw = BITBLTBUF.SBW; int sx = TRXPOS.SSAX; int w = TRXREG.RRW; GSOffset off = mem.GetOffset(bp, bw, BITBLTBUF.SPSM); // printf("spsm=%d x=%d ex=%d y=%d len=%d\n", BITBLTBUF.SPSM, x, ex, y, len); switch (BITBLTBUF.SPSM) { case PSMCT32: case PSMZ32: { // MGS1 intro, fade effect between two scenes (airplane outside-inside transition) int x = tx; int y = ty; int ex = sx + w; len /= 4; u32* vm = mem.vm32(); GSOffset::PAHelper pa = off.assertSizesMatch(GSLocalMemory::swizzle32).paMulti(0, y); while (len > 0) { for (; len > 0 && x < ex && (x & 7); len--, x++, pd++) { *pd = vm[pa.value(x)]; } // aligned to a column for (int ex8 = ex - 8; len >= 8 && x <= ex8; len -= 8, x += 8, pd += 8) { u32* ps = &vm[pa.value(x)]; GSVector4i::store(&pd[0], GSVector4i::load(ps + 0, ps + 4)); GSVector4i::store(&pd[4], GSVector4i::load(ps + 8, ps + 12)); for (int i = 0; i < 8; i++) pxAssert(pd[i] == vm[pa.value(x + i)]); } for (; len > 0 && x < ex; len--, x++, pd++) { *pd = vm[pa.value(x)]; } if (x == ex) { y++; x = sx; pa = off.assertSizesMatch(GSLocalMemory::swizzle32).paMulti(0, y); } } tx = x; ty = y; } break; case PSMCT24: case PSMZ24: readWriteHelper(tx, ty, len / 3, 1, sx, w, off.assertSizesMatch(GSLocalMemory::swizzle32), [&](GSOffset::PAHelper& pa, int x) { u32 c = vm32[pa.value(x)]; pb[0] = (u8)(c); pb[1] = (u8)(c >> 8); pb[2] = (u8)(c >> 16); pb += 3; }); break; case PSMCT16: case PSMCT16S: case PSMZ16: case PSMZ16S: readWriteHelper(tx, ty, len / 2, 1, sx, w, off.assertSizesMatch(GSLocalMemory::swizzle16), [&](GSOffset::PAHelper& pa, int x) { *pw = vm16[pa.value(x)]; pw++; }); break; case PSMT8: readWriteHelper(tx, ty, len, 1, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT8), [&](GSOffset::PAHelper& pa, int x) { *pb = vm8[pa.value(x)]; pb++; }); break; case PSMT4: readWriteHelper(tx, ty, len * 2, 2, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT4), [&](GSOffset::PAHelper& pa, int x) { u8 low = mem.ReadPixel4(pa.value(x)); u8 high = mem.ReadPixel4(pa.value(x + 1)); *pb = low | (high << 4); }); break; case PSMT8H: readWriteHelper(tx, ty, len, 1, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT8H), [&](GSOffset::PAHelper& pa, int x) { *pb = (u8)(vm32[pa.value(x)] >> 24); pb++; }); break; case PSMT4HL: readWriteHelper(tx, ty, len * 2, 2, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT4HL), [&](GSOffset::PAHelper& pa, int x) { u32 c0 = vm32[pa.value(x)] >> 24 & 0x0f; u32 c1 = vm32[pa.value(x + 1)] >> 20 & 0xf0; *pb = (u8)(c0 | c1); pb++; }); break; case PSMT4HH: readWriteHelper(tx, ty, len * 2, 2, sx, w, GSOffset::fromKnownPSM(bp, bw, PSMT4HH), [&](GSOffset::PAHelper& pa, int x) { u32 c0 = vm32[pa.value(x)] >> 28 & 0x0f; u32 c1 = vm32[pa.value(x + 1)] >> 24 & 0xf0; *pb = (u8)(c0 | c1); pb++; }); break; } } /////////////////// void GSLocalMemoryFunctions::ReadTexture32(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadBlock32(src, read_dst, dstpitch); }); } void GSLocalMemoryFunctions::ReadTexture24(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { if (TEXA.AEM) { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock24(src, read_dst, dstpitch, TEXA); }); } else { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock24(src, read_dst, dstpitch, TEXA); }); } } void GSLocalMemoryFunctions::ReadTextureGPU24(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle16), mem, r, dst, dstpitch, 16, [&](u8* read_dst, const u8* src) { GSBlock::ReadBlock16(src, read_dst, dstpitch); }); // Convert packed RGB scanline to 32 bits RGBA pxAssert(dstpitch >= r.width() * 4); for (int y = r.top; y < r.bottom; y++) { u8* line = dst + y * dstpitch; for (int x = r.right; x >= r.left; x--) { *(u32*)&line[x * 4] = *(u32*)&line[x * 3] & 0xFFFFFF; } } } void GSLocalMemoryFunctions::ReadTexture16(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { if (TEXA.AEM) { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle16), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock16(src, read_dst, dstpitch, TEXA); }); } else { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle16), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock16(src, read_dst, dstpitch, TEXA); }); } } void GSLocalMemoryFunctions::ReadTexture8(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { const u32* pal = mem.m_clut; foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle8), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock8_32(src, read_dst, dstpitch, pal); }); } void GSLocalMemoryFunctions::ReadTexture4(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { const u32* pal = mem.m_clut; foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle4), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock4_32(src, read_dst, dstpitch, pal); }); } void GSLocalMemoryFunctions::ReadTexture8H(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { const u32* pal = mem.m_clut; foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock8H_32(src, read_dst, dstpitch, pal); }); } #if _M_SSE == 0x501 void GSLocalMemoryFunctions::ReadTexture8HSW(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { const u32* pal = mem.m_clut; foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle8), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock8_32HSW(src, read_dst, dstpitch, pal); }); } void GSLocalMemoryFunctions::ReadTexture8HHSW(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { const u32* pal = mem.m_clut; foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock8H_32HSW(src, read_dst, dstpitch, pal); }); } #endif void GSLocalMemoryFunctions::ReadTexture4HL(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { const u32* pal = mem.m_clut; foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock4HL_32(src, read_dst, dstpitch, pal); }); } void GSLocalMemoryFunctions::ReadTexture4HH(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { const u32* pal = mem.m_clut; foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 32, [&](u8* read_dst, const u8* src) { GSBlock::ReadAndExpandBlock4HH_32(src, read_dst, dstpitch, pal); }); } /////////////////// void GSLocalMemoryFunctions::ReadTextureBlock32(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadBlock32(mem.BlockPtr(bp), dst, dstpitch); } void GSLocalMemoryFunctions::ReadTextureBlock24(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); if (TEXA.AEM) { GSBlock::ReadAndExpandBlock24(mem.BlockPtr(bp), dst, dstpitch, TEXA); } else { GSBlock::ReadAndExpandBlock24(mem.BlockPtr(bp), dst, dstpitch, TEXA); } } void GSLocalMemoryFunctions::ReadTextureBlock16(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); if (TEXA.AEM) { GSBlock::ReadAndExpandBlock16(mem.BlockPtr(bp), dst, dstpitch, TEXA); } else { GSBlock::ReadAndExpandBlock16(mem.BlockPtr(bp), dst, dstpitch, TEXA); } } void GSLocalMemoryFunctions::ReadTextureBlock8(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadAndExpandBlock8_32(mem.BlockPtr(bp), dst, dstpitch, mem.m_clut); } void GSLocalMemoryFunctions::ReadTextureBlock4(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadAndExpandBlock4_32(mem.BlockPtr(bp), dst, dstpitch, mem.m_clut); } void GSLocalMemoryFunctions::ReadTextureBlock8H(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadAndExpandBlock8H_32(mem.BlockPtr(bp), dst, dstpitch, mem.m_clut); } #if _M_SSE == 0x501 void GSLocalMemoryFunctions::ReadTextureBlock8HSW(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadAndExpandBlock8_32HSW(mem.BlockPtr(bp), dst, dstpitch, mem.m_clut); } void GSLocalMemoryFunctions::ReadTextureBlock8HHSW(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadAndExpandBlock8H_32HSW(mem.BlockPtr(bp), dst, dstpitch, mem.m_clut); } #endif void GSLocalMemoryFunctions::ReadTextureBlock4HL(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadAndExpandBlock4HL_32(mem.BlockPtr(bp), dst, dstpitch, mem.m_clut); } void GSLocalMemoryFunctions::ReadTextureBlock4HH(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadAndExpandBlock4HH_32(mem.BlockPtr(bp), dst, dstpitch, mem.m_clut); } // 32/8 void GSLocalMemoryFunctions::ReadTexture8P(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle8), mem, r, dst, dstpitch, 8, [&](u8* read_dst, const u8* src) { GSBlock::ReadBlock8(src, read_dst, dstpitch); }); } void GSLocalMemoryFunctions::ReadTexture4P(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle4), mem, r, dst, dstpitch, 8, [&](u8* read_dst, const u8* src) { GSBlock::ReadBlock4P(src, read_dst, dstpitch); }); } void GSLocalMemoryFunctions::ReadTexture8HP(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 8, [&](u8* read_dst, const u8* src) { GSBlock::ReadBlock8HP(src, read_dst, dstpitch); }); } void GSLocalMemoryFunctions::ReadTexture4HLP(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 8, [&](u8* read_dst, const u8* src) { GSBlock::ReadBlock4HLP(src, read_dst, dstpitch); }); } void GSLocalMemoryFunctions::ReadTexture4HHP(GSLocalMemory& mem, const GSOffset& off, const GSVector4i& r, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { foreachBlock(off.assertSizesMatch(GSLocalMemory::swizzle32), mem, r, dst, dstpitch, 8, [&](u8* read_dst, const u8* src) { GSBlock::ReadBlock4HHP(src, read_dst, dstpitch); }); } // void GSLocalMemoryFunctions::ReadTextureBlock8P(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { GSBlock::ReadBlock8(mem.BlockPtr(bp), dst, dstpitch); } void GSLocalMemoryFunctions::ReadTextureBlock4P(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadBlock4P(mem.BlockPtr(bp), dst, dstpitch); } void GSLocalMemoryFunctions::ReadTextureBlock8HP(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadBlock8HP(mem.BlockPtr(bp), dst, dstpitch); } void GSLocalMemoryFunctions::ReadTextureBlock4HLP(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadBlock4HLP(mem.BlockPtr(bp), dst, dstpitch); } void GSLocalMemoryFunctions::ReadTextureBlock4HHP(const GSLocalMemory& mem, u32 bp, u8* dst, int dstpitch, const GIFRegTEXA& TEXA) { ALIGN_STACK(32); GSBlock::ReadBlock4HHP(mem.BlockPtr(bp), dst, dstpitch); }