diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp index 6f24f50e73..9febde65af 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -257,6 +257,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* connect(m_ui.enableHWFixes, &QCheckBox::stateChanged, this, &GraphicsSettingsWidget::onEnableHardwareFixesChanged); updateRendererDependentOptions(); + // only allow disabling readbacks for per-game settings, it's too dangerous + m_ui.disableHardwareReadbacks->setEnabled(m_dialog->isPerGameSettings()); + dialog->registerWidgetHelp(m_ui.enableHWFixes, tr("Manual Hardware Renderer Fixes"), tr("Unchecked"), tr("Enabling this option gives you the ability to change the renderer and upscaling fixes " "to your games. However IF you have ENABLED this, you WILL DISABLE AUTOMATIC " @@ -271,6 +274,10 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* tr("Detects when idle frames are being presented in 25/30fps games, and skips presenting those frames. The frame is still rendered, it just means " "the GPU has more time to complete it (this is NOT frame skipping). Can smooth our frame time fluctuations when the CPU/GPU are near maximum " "utilization, but makes frame pacing more inconsistent and can increase input lag.")); + dialog->registerWidgetHelp(m_ui.disableHardwareReadbacks, tr("Disable Hardware Readbacks"), tr("Unchecked"), + tr("Skips synchronizing with the GS thread and host GPU for GS downloads. " + "Can result in a large speed boost on slower systems, at the cost of many broken graphical effects. " + "If games are broken and you have this option enabled, please disable it first.")); } GraphicsSettingsWidget::~GraphicsSettingsWidget() = default; diff --git a/pcsx2/GS/GS.cpp b/pcsx2/GS/GS.cpp index 6677dc9914..06101ff85b 100644 --- a/pcsx2/GS/GS.cpp +++ b/pcsx2/GS/GS.cpp @@ -423,6 +423,11 @@ void GSInitAndReadFIFO(u8* mem, u32 size) } } +void GSReadLocalMemoryUnsync(u8* mem, u32 qwc, u64 BITBLITBUF, u64 TRXPOS, u64 TRXREG) +{ + g_gs_renderer->ReadLocalMemoryUnsync(mem, qwc, GIFRegBITBLTBUF{BITBLITBUF}, GIFRegTRXPOS{TRXPOS}, GIFRegTRXREG{TRXREG}); +} + void GSgifTransfer(const u8* mem, u32 size) { try diff --git a/pcsx2/GS/GS.h b/pcsx2/GS/GS.h index 48c2996181..43830ae9d3 100644 --- a/pcsx2/GS/GS.h +++ b/pcsx2/GS/GS.h @@ -62,6 +62,7 @@ void GSclose(); void GSgifSoftReset(u32 mask); void GSwriteCSR(u32 csr); void GSInitAndReadFIFO(u8* mem, u32 size); +void GSReadLocalMemoryUnsync(u8* mem, u32 qwc, u64 BITBLITBUF, u64 TRXPOS, u64 TRXREG); void GSgifTransfer(const u8* mem, u32 size); void GSgifTransfer1(u8* mem, u32 addr); void GSgifTransfer2(u8* mem, u32 size); diff --git a/pcsx2/GS/GSState.cpp b/pcsx2/GS/GSState.cpp index ee21e9d35b..ecf8cfa6e6 100644 --- a/pcsx2/GS/GSState.cpp +++ b/pcsx2/GS/GSState.cpp @@ -2091,6 +2091,25 @@ void GSState::ReadFIFO(u8* mem, int size) m_dump->ReadFIFO(size); } +void GSState::ReadLocalMemoryUnsync(u8* mem, int qwc, GIFRegBITBLTBUF BITBLTBUF, GIFRegTRXPOS TRXPOS, GIFRegTRXREG TRXREG) +{ + const int sx = TRXPOS.SSAX; + const int sy = TRXPOS.SSAY; + const int w = TRXREG.RRW; + const int h = TRXREG.RRH; + + const u16 bpp = GSLocalMemory::m_psm[BITBLTBUF.SPSM].trbpp; + + GSTransferBuffer tb; + tb.Init(TRXPOS.SSAX, TRXPOS.SSAY, BITBLTBUF); + + int len = qwc * 16; + if (!tb.Update(w, h, bpp, len)) + return; + + m_mem.ReadImageX(tb.x, tb.y, mem, len, BITBLTBUF, TRXPOS, TRXREG); +} + template void GSState::Transfer<0>(const u8* mem, u32 size); template void GSState::Transfer<1>(const u8* mem, u32 size); template void GSState::Transfer<2>(const u8* mem, u32 size); diff --git a/pcsx2/GS/GSState.h b/pcsx2/GS/GSState.h index aef7b79632..fcf7884835 100644 --- a/pcsx2/GS/GSState.h +++ b/pcsx2/GS/GSState.h @@ -322,6 +322,7 @@ public: void SoftReset(u32 mask); void WriteCSR(u32 csr) { m_regs->CSR.U32[1] = csr; } void ReadFIFO(u8* mem, int size); + void ReadLocalMemoryUnsync(u8* mem, int qwc, GIFRegBITBLTBUF BITBLTBUF, GIFRegTRXPOS TRXPOS, GIFRegTRXREG TRXREG); template void Transfer(const u8* mem, u32 size); int Freeze(freezeData* fd, bool sizeonly); int Defrost(const freezeData* fd); diff --git a/pcsx2/Gif_Unit.cpp b/pcsx2/Gif_Unit.cpp index b30825b1f5..a849a88890 100644 --- a/pcsx2/Gif_Unit.cpp +++ b/pcsx2/Gif_Unit.cpp @@ -27,15 +27,11 @@ bool Gif_HandlerAD(u8* pMem) { u32 reg = pMem[8]; u32* data = (u32*)pMem; - if (reg == 0x50) + if (reg >= GIF_A_D_REG_BITBLTBUF && reg <= GIF_A_D_REG_TRXREG) { - vif1.BITBLTBUF._u64 = *(u64*)pMem; + vif1.transfer_registers[reg - GIF_A_D_REG_BITBLTBUF] = *(u64*)pMem; } - else if (reg == 0x52) - { - vif1.TRXREG._u64 = *(u64*)pMem; - } - else if (reg == 0x53) + else if (reg == GIF_A_D_REG_TRXDIR) { // TRXDIR if ((pMem[0] & 3) == 1) { // local -> host @@ -63,7 +59,7 @@ bool Gif_HandlerAD(u8* pMem) vif1.GSLastDownloadSize = vif1.TRXREG.RRW * vif1.TRXREG.RRH * bpp >> 7; } } - else if (reg == 0x60) + else if (reg == GIF_A_D_REG_SIGNAL) { // SIGNAL if (CSRreg.SIGNAL) { // Time to ignore all subsequent drawing operations. @@ -85,12 +81,12 @@ bool Gif_HandlerAD(u8* pMem) CSRreg.SIGNAL = true; } } - else if (reg == 0x61) + else if (reg == GIF_A_D_REG_FINISH) { // FINISH GUNIT_WARN("GIF Handler - FINISH"); CSRreg.FINISH = true; } - else if (reg == 0x62) + else if (reg == GIF_A_D_REG_LABEL) { // LABEL GUNIT_WARN("GIF Handler - LABEL"); GSSIGLBLID.LBLID = (GSSIGLBLID.LBLID & ~data[1]) | (data[0] & data[1]); @@ -108,7 +104,7 @@ bool Gif_HandlerAD_MTVU(u8* pMem) u32 reg = pMem[8]; u32* data = (u32*)pMem; - if (reg == 0x60) + if (reg == GIF_A_D_REG_SIGNAL) { // SIGNAL GUNIT_WARN("GIF Handler - SIGNAL"); if (vu1Thread.mtvuInterrupts.load(std::memory_order_acquire) & VU_Thread::InterruptFlagSignal) @@ -116,14 +112,14 @@ bool Gif_HandlerAD_MTVU(u8* pMem) vu1Thread.gsSignal.store(((u64)data[1] << 32) | data[0], std::memory_order_relaxed); vu1Thread.mtvuInterrupts.fetch_or(VU_Thread::InterruptFlagSignal, std::memory_order_release); } - else if (reg == 0x61) + else if (reg == GIF_A_D_REG_FINISH) { // FINISH GUNIT_WARN("GIF Handler - FINISH"); u32 old = vu1Thread.mtvuInterrupts.fetch_or(VU_Thread::InterruptFlagFinish, std::memory_order_relaxed); if (old & VU_Thread::InterruptFlagFinish) Console.Error("GIF Handler MTVU - Double FINISH Not Handled"); } - else if (reg == 0x62) + else if (reg == GIF_A_D_REG_LABEL) { // LABEL GUNIT_WARN("GIF Handler - LABEL"); // It's okay to coalesce label updates diff --git a/pcsx2/MTGS.cpp b/pcsx2/MTGS.cpp index 243546ff0a..7ee43173da 100644 --- a/pcsx2/MTGS.cpp +++ b/pcsx2/MTGS.cpp @@ -244,7 +244,10 @@ void SysMtgsThread::PostVsyncStart(bool registers_written) void SysMtgsThread::InitAndReadFIFO(u8* mem, u32 qwc) { if (GSConfig.HWDisableReadbacks && GSConfig.UseHardwareRenderer()) + { + GSReadLocalMemoryUnsync(mem, qwc, vif1.BITBLTBUF._u64, vif1.TRXPOS._u64, vif1.TRXREG._u64); return; + } SendPointerPacket(GS_RINGTYPE_INIT_AND_READ_FIFO, qwc, mem); WaitGS(false, false, false); diff --git a/pcsx2/Vif_Dma.h b/pcsx2/Vif_Dma.h index 25ef35a0ea..ef3e3ffa20 100644 --- a/pcsx2/Vif_Dma.h +++ b/pcsx2/Vif_Dma.h @@ -44,9 +44,27 @@ union tBITBLTBUF { }; }; -union tTRXREG { +union tTRXPOS { u64 _u64; struct { + u32 SSAX : 11; + u32 _PAD1 : 5; + u32 SSAY : 11; + u32 _PAD2 : 5; + u32 DSAX : 11; + u32 _PAD3 : 5; + u32 DSAY : 11; + u32 DIRY : 1; + u32 DIRX : 1; + u32 _PAD4 : 3; + }; +}; + +union tTRXREG +{ + u64 _u64; + struct + { u32 RRW : 12; u32 _pad12 : 20; u32 RRH : 12; @@ -83,8 +101,17 @@ struct vifStruct { int unpackcalls; // GS registers used for calculating the size of the last local->host transfer initiated on the GS // Transfer size calculation should be restricted to GS emulation in the future - tBITBLTBUF BITBLTBUF; - tTRXREG TRXREG; + union + { + struct + { + tBITBLTBUF BITBLTBUF; + tTRXPOS TRXPOS; + tTRXREG TRXREG; + }; + u64 transfer_registers[3]; + }; + u32 GSLastDownloadSize; tVIF_CTRL irqoffset; // 32bit offset where next vif code is