#include "GsTransferInvalidationTest.h" #include "gs/GSHandler.h" #include "gs/GsPixelFormats.h" #include "gs/GsTransferRange.h" static CGSHandler::BITBLTBUF MakeSrcBltBuf(uint32 psm, uint32 bufPtr, uint32 bufWidth) { assert((bufPtr & 0xFF) == 0); assert((bufWidth & 0x3F) == 0); auto bltBuf = make_convertible(0); bltBuf.nSrcPsm = psm; bltBuf.nSrcPtr = bufPtr / 0x100; bltBuf.nSrcWidth = bufWidth / 0x40; return bltBuf; } static CGSHandler::BITBLTBUF MakeDstBltBuf(uint32 psm, uint32 bufPtr, uint32 bufWidth) { assert((bufPtr & 0xFF) == 0); assert((bufWidth & 0x3F) == 0); auto bltBuf = make_convertible(0); bltBuf.nDstPsm = psm; bltBuf.nDstPtr = bufPtr / 0x100; bltBuf.nDstWidth = bufWidth / 0x40; return bltBuf; } static CGSHandler::TRXREG MakeTrxReg(uint32 width, uint32 height) { auto trxReg = make_convertible(0); trxReg.nRRW = width; trxReg.nRRH = height; return trxReg; } static CGSHandler::TRXPOS MakeSrcTrxPos(uint32 x, uint32 y) { auto trxPos = make_convertible(0); trxPos.nSSAX = x; trxPos.nSSAY = y; return trxPos; } static CGSHandler::TRXPOS MakeDstTrxPos(uint32 x, uint32 y) { auto trxPos = make_convertible(0); trxPos.nDSAX = x; trxPos.nDSAY = y; return trxPos; } template static void SinglePageTransferTest() { uint32 bufPtr = 0x300000; uint32 bufWidth = Storage::PAGEWIDTH; auto bltBuf = MakeDstBltBuf(psm, bufPtr, bufWidth); auto trxReg = MakeTrxReg(Storage::PAGEWIDTH, Storage::PAGEHEIGHT); auto trxPos = make_convertible(0); auto [transferAddress, transferSize] = GsTransfer::GetDstRange(bltBuf, trxReg, trxPos); TEST_VERIFY(transferAddress == bufPtr); TEST_VERIFY(transferSize == CGsPixelFormats::PAGESIZE); } template static void SinglePageOffsetTransferTest(uint32 offsetX, uint32 offsetY) { uint32 bufPtr = 0x11A000; uint32 bufWidth = 640; assert(offsetX <= bufWidth); uint32 pageOffsetX = offsetX / Storage::PAGEWIDTH; uint32 pageOffsetY = offsetY / Storage::PAGEHEIGHT; uint32 pagePitch = bufWidth / Storage::PAGEWIDTH; auto bltBuf = MakeSrcBltBuf(psm, bufPtr, bufWidth); auto trxReg = MakeTrxReg(1, 4); auto trxPos = MakeSrcTrxPos(offsetX, offsetY); auto [transferAddress, transferSize] = GsTransfer::GetSrcRange(bltBuf, trxReg, trxPos); TEST_VERIFY(transferAddress == bufPtr + (((pagePitch * pageOffsetY) + pageOffsetX) * CGsPixelFormats::PAGESIZE)); TEST_VERIFY(transferSize == CGsPixelFormats::PAGESIZE); } template static void MultiPageTransferTest() { uint32 bufPtr = 0x120000; uint32 bufWidth = 512; uint32 transferHeight = 256; uint32 pageCountY = transferHeight / Storage::PAGEHEIGHT; uint32 pagePitch = bufWidth / Storage::PAGEWIDTH; auto bltBuf = MakeDstBltBuf(psm, bufPtr, bufWidth); auto trxReg = MakeTrxReg(256, transferHeight); auto trxPos = MakeDstTrxPos(16, 16); auto [transferAddress, transferSize] = GsTransfer::GetDstRange(bltBuf, trxReg, trxPos); TEST_VERIFY(transferAddress == bufPtr); TEST_VERIFY(transferSize == (pageCountY + 1) * pagePitch * CGsPixelFormats::PAGESIZE); } static void SimpleOffsetYTransferTest() { //In this test, the transfer position is skipping a few pages //transferAddress should start at (bufWidth / 64) * 2 //This is used for movie playback in many games uint32 bufPtr = 0x100000; uint32 bufWidth = 640; uint32 pageCountX = bufWidth / CGsPixelFormats::STORAGEPSMCT32::PAGEWIDTH; uint32 pageOffsetY = 2; auto bltBuf = MakeDstBltBuf(CGSHandler::PSMCT32, bufPtr, bufWidth); auto trxReg = MakeTrxReg(16, 16); auto trxPos = MakeDstTrxPos(0, CGsPixelFormats::STORAGEPSMCT32::PAGEHEIGHT * pageOffsetY); auto [transferAddress, transferSize] = GsTransfer::GetDstRange(bltBuf, trxReg, trxPos); TEST_VERIFY(transferAddress == (bufPtr + (CGsPixelFormats::PAGESIZE * pageCountX * pageOffsetY))); TEST_VERIFY(transferSize == CGsPixelFormats::PAGESIZE); } static void StraddleOffsetYTransferTest() { //Based on FF12 opening movie (when you start a new game) uint32 bufPtr = 0; uint32 bufWidth = 512; auto bltBuf = MakeDstBltBuf(CGSHandler::PSMCT32, bufPtr, bufWidth); auto trxReg = MakeTrxReg(16, 16); auto trxPos = MakeDstTrxPos(0, 1394); auto [transferAddress, transferSize] = GsTransfer::GetDstRange(bltBuf, trxReg, trxPos); TEST_VERIFY(transferAddress == 0x002B0000); TEST_VERIFY(transferSize == 0x00020000); } static void ZeroBufWidthTransferTest() { //This is done by DBZ: Tenkaichi 2 //This transfer should touch a page at least uint32 bufPtr = 0x380000; uint32 bufWidth = 0; auto bltBuf = MakeDstBltBuf(CGSHandler::PSMCT32, bufPtr, bufWidth); auto trxReg = MakeTrxReg(16, 16); auto trxPos = make_convertible(0); auto [transferAddress, transferSize] = GsTransfer::GetDstRange(bltBuf, trxReg, trxPos); TEST_VERIFY(transferAddress == bufPtr); TEST_VERIFY(transferSize == CGsPixelFormats::PAGESIZE); } void EspgaludaTransferTest() { uint32 bufPtr = 0x320000; uint32 bufWidth = 1024; auto bltBuf = MakeDstBltBuf(CGSHandler::PSMT8, bufPtr, bufWidth); auto trxReg = MakeTrxReg(160, 128); auto trxPos = MakeDstTrxPos(16, 16); auto [transferAddress, transferSize] = GsTransfer::GetDstRange(bltBuf, trxReg, trxPos); //PSMT8 page size is 128x64 //This transfer spans 8 pages on the X dimension -> ceil(bufWidth / psm8.pageWidth) -> ceil(1024 / 128) //This transfer also touches 3 pages on the Y dimension -> ceil((trxReg.y + trxPos.dy) / psm8.pageHeight) -> ceil((128 + 16) / 64) TEST_VERIFY(transferAddress == bufPtr); TEST_VERIFY(transferSize == (8 * 3) * CGsPixelFormats::PAGESIZE); } void CGsTransferInvalidationTest::Execute() { SinglePageTransferTest(); SinglePageTransferTest(); SinglePageTransferTest(); SinglePageOffsetTransferTest(158, 259); SinglePageOffsetTransferTest(432, 780); MultiPageTransferTest(); MultiPageTransferTest(); SimpleOffsetYTransferTest(); StraddleOffsetYTransferTest(); ZeroBufWidthTransferTest(); EspgaludaTransferTest(); }