mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-28 09:24:41 -04:00
feat: cheap copy from host
feat: small perf o vsync tick
This commit is contained in:
@@ -383,6 +383,7 @@ public:
|
||||
const EeScheduler &eeScheduler() const;
|
||||
void postEeEvent(EeEvent event);
|
||||
bool eeCheckpointDue() const noexcept;
|
||||
[[noreturn]] void eeWaitVSyncTicks(uint32_t ticks, uint32_t resumePc);
|
||||
|
||||
struct EeExitHandlerRegistration
|
||||
{
|
||||
|
||||
@@ -409,6 +409,7 @@ private:
|
||||
std::atomic<bool> m_guestExecuting{false};
|
||||
std::atomic<bool> m_stopRequested{false};
|
||||
std::atomic<bool> m_checkpointPending{false};
|
||||
uint32_t m_debugPublishCountdown = 0u;
|
||||
|
||||
mutable std::mutex m_eventMutex;
|
||||
std::condition_variable m_eventCv;
|
||||
|
||||
@@ -364,7 +364,14 @@ public:
|
||||
|
||||
private:
|
||||
void snapshotVRAM();
|
||||
void writeRegisterUnlocked(uint8_t regAddr, uint64_t value);
|
||||
void writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi);
|
||||
void uploadImageNativeUnlocked(uint64_t bitbltbuf,
|
||||
uint64_t trxpos,
|
||||
uint64_t trxreg,
|
||||
uint64_t trxdir,
|
||||
const uint8_t *data,
|
||||
uint32_t sizeBytes);
|
||||
void vertexKick(bool drawing);
|
||||
void latchHostPresentationFrameUnlocked();
|
||||
|
||||
@@ -396,6 +403,7 @@ private:
|
||||
uint32_t m_vramSize = 0;
|
||||
struct GSRegisters *m_privRegs = nullptr;
|
||||
mutable std::recursive_mutex m_stateMutex;
|
||||
mutable std::mutex m_presentationMutex;
|
||||
|
||||
GSContext m_ctx[2];
|
||||
GSPrimReg m_prim{};
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace
|
||||
constexpr auto kVBlankPeriod = std::chrono::microseconds(16667);
|
||||
constexpr auto kVBlankDuration = std::chrono::microseconds(500);
|
||||
constexpr uint64_t kAlarmTickMicroseconds = 64u;
|
||||
constexpr uint32_t kDebugPublishDispatchInterval = 4096u;
|
||||
|
||||
template <typename Map>
|
||||
int allocatePositiveId(int &nextId, const Map &objects)
|
||||
@@ -92,6 +93,7 @@ void EeScheduler::reset(uint8_t *rdram, const R5900Context &mainContext)
|
||||
m_insideInterrupt = false;
|
||||
m_stopRequested.store(false, std::memory_order_release);
|
||||
m_checkpointPending.store(false, std::memory_order_release);
|
||||
m_debugPublishCountdown = 0u;
|
||||
{
|
||||
std::lock_guard lock(m_eventMutex);
|
||||
m_events.clear();
|
||||
@@ -184,8 +186,16 @@ void EeScheduler::run()
|
||||
}
|
||||
}
|
||||
R5900Context &context = running->activeContext();
|
||||
copyMainContextToRuntime();
|
||||
publishSnapshot();
|
||||
if (m_debugPublishCountdown == 0u)
|
||||
{
|
||||
copyMainContextToRuntime();
|
||||
publishSnapshot();
|
||||
m_debugPublishCountdown = kDebugPublishDispatchInterval - 1u;
|
||||
}
|
||||
else
|
||||
{
|
||||
--m_debugPublishCountdown;
|
||||
}
|
||||
|
||||
m_runtime.m_debugPc.store(context.pc, std::memory_order_relaxed);
|
||||
m_runtime.m_debugRa.store(getRegU32(&context, 31), std::memory_order_relaxed);
|
||||
|
||||
@@ -490,13 +490,16 @@ void GS::reset()
|
||||
m_preferredDisplaySourceFrame = {};
|
||||
m_preferredDisplayDestFbp = 0;
|
||||
m_hasPreferredDisplaySource = false;
|
||||
m_hostPresentationFrame.clear();
|
||||
m_hostPresentationWidth = 0u;
|
||||
m_hostPresentationHeight = 0u;
|
||||
m_hostPresentationDisplayFbp = 0u;
|
||||
m_hostPresentationSourceFbp = 0u;
|
||||
m_hostPresentationUsedPreferred = false;
|
||||
m_hasHostPresentationFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> presentationLock(m_presentationMutex);
|
||||
m_hostPresentationFrame.clear();
|
||||
m_hostPresentationWidth = 0u;
|
||||
m_hostPresentationHeight = 0u;
|
||||
m_hostPresentationDisplayFbp = 0u;
|
||||
m_hostPresentationSourceFbp = 0u;
|
||||
m_hostPresentationUsedPreferred = false;
|
||||
m_hasHostPresentationFrame = false;
|
||||
}
|
||||
|
||||
m_debugHistoryWrite = 0;
|
||||
m_debugHistoryCount = 0;
|
||||
@@ -562,12 +565,15 @@ GSDebugSnapshot GS::getDebugSnapshot() const
|
||||
snapshot.preferredDisplaySourceFrame = m_preferredDisplaySourceFrame;
|
||||
snapshot.preferredDisplayDestFbp = m_preferredDisplayDestFbp;
|
||||
snapshot.hasPreferredDisplaySource = m_hasPreferredDisplaySource;
|
||||
snapshot.hostPresentationWidth = m_hostPresentationWidth;
|
||||
snapshot.hostPresentationHeight = m_hostPresentationHeight;
|
||||
snapshot.hostPresentationDisplayFbp = m_hostPresentationDisplayFbp;
|
||||
snapshot.hostPresentationSourceFbp = m_hostPresentationSourceFbp;
|
||||
snapshot.hostPresentationUsedPreferred = m_hostPresentationUsedPreferred;
|
||||
snapshot.hasHostPresentationFrame = m_hasHostPresentationFrame;
|
||||
{
|
||||
std::lock_guard<std::mutex> presentationLock(m_presentationMutex);
|
||||
snapshot.hostPresentationWidth = m_hostPresentationWidth;
|
||||
snapshot.hostPresentationHeight = m_hostPresentationHeight;
|
||||
snapshot.hostPresentationDisplayFbp = m_hostPresentationDisplayFbp;
|
||||
snapshot.hostPresentationSourceFbp = m_hostPresentationSourceFbp;
|
||||
snapshot.hostPresentationUsedPreferred = m_hostPresentationUsedPreferred;
|
||||
snapshot.hasHostPresentationFrame = m_hasHostPresentationFrame;
|
||||
}
|
||||
snapshot.localToHostPendingBytes = (m_localToHostReadPos < m_localToHostBuffer.size())
|
||||
? (m_localToHostBuffer.size() - m_localToHostReadPos)
|
||||
: 0u;
|
||||
@@ -959,8 +965,100 @@ bool GS::copyFrameToHostRgbaUnlocked(const GSFrameReg &frame,
|
||||
|
||||
void GS::latchHostPresentationFrame()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
latchHostPresentationFrameUnlocked();
|
||||
thread_local std::vector<uint8_t> vramSnapshot;
|
||||
thread_local GS presentationGs;
|
||||
|
||||
thread_local GSRegisters privateRegisters{};
|
||||
GSFrameReg contextFrames[2]{};
|
||||
GSFrameReg preferredSource{};
|
||||
uint32_t preferredDestFbp = 0u;
|
||||
bool hasPreferredSource = false;
|
||||
uint32_t vramSize = 0u;
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
if (!m_privRegs || !m_vram || m_vramSize == 0u)
|
||||
{
|
||||
std::lock_guard<std::mutex> presentationLock(m_presentationMutex);
|
||||
m_hostPresentationFrame.clear();
|
||||
m_hostPresentationWidth = 0u;
|
||||
m_hostPresentationHeight = 0u;
|
||||
m_hostPresentationDisplayFbp = 0u;
|
||||
m_hostPresentationSourceFbp = 0u;
|
||||
m_hostPresentationUsedPreferred = false;
|
||||
m_hasHostPresentationFrame = false;
|
||||
return;
|
||||
}
|
||||
|
||||
vramSize = m_vramSize;
|
||||
vramSnapshot.resize(vramSize);
|
||||
std::memcpy(vramSnapshot.data(), m_vram, vramSize);
|
||||
|
||||
privateRegisters.pmode = m_privRegs->pmode;
|
||||
privateRegisters.smode1 = m_privRegs->smode1;
|
||||
privateRegisters.smode2 = m_privRegs->smode2;
|
||||
privateRegisters.srfsh = m_privRegs->srfsh;
|
||||
privateRegisters.synch1 = m_privRegs->synch1;
|
||||
privateRegisters.synch2 = m_privRegs->synch2;
|
||||
privateRegisters.syncv = m_privRegs->syncv;
|
||||
privateRegisters.dispfb1 = m_privRegs->dispfb1;
|
||||
privateRegisters.display1 = m_privRegs->display1;
|
||||
privateRegisters.dispfb2 = m_privRegs->dispfb2;
|
||||
privateRegisters.display2 = m_privRegs->display2;
|
||||
privateRegisters.extbuf = m_privRegs->extbuf;
|
||||
privateRegisters.extdata = m_privRegs->extdata;
|
||||
privateRegisters.extwrite = m_privRegs->extwrite;
|
||||
privateRegisters.bgcolor = m_privRegs->bgcolor;
|
||||
privateRegisters.csr.store(m_privRegs->csr.load(std::memory_order_acquire), std::memory_order_relaxed);
|
||||
privateRegisters.vsyncTick.store(m_privRegs->vsyncTick.load(std::memory_order_acquire), std::memory_order_relaxed);
|
||||
privateRegisters.imr = m_privRegs->imr;
|
||||
privateRegisters.busdir = m_privRegs->busdir;
|
||||
privateRegisters.siglblid = m_privRegs->siglblid;
|
||||
|
||||
contextFrames[0] = m_ctx[0].frame;
|
||||
contextFrames[1] = m_ctx[1].frame;
|
||||
preferredSource = m_preferredDisplaySourceFrame;
|
||||
preferredDestFbp = m_preferredDisplayDestFbp;
|
||||
hasPreferredSource = m_hasPreferredDisplaySource;
|
||||
}
|
||||
|
||||
presentationGs.init(vramSnapshot.data(), vramSize, &privateRegisters);
|
||||
presentationGs.m_ctx[0].frame = contextFrames[0];
|
||||
presentationGs.m_ctx[1].frame = contextFrames[1];
|
||||
presentationGs.m_preferredDisplaySourceFrame = preferredSource;
|
||||
presentationGs.m_preferredDisplayDestFbp = preferredDestFbp;
|
||||
presentationGs.m_hasPreferredDisplaySource = hasPreferredSource;
|
||||
presentationGs.latchHostPresentationFrameUnlocked();
|
||||
|
||||
uint32_t displayFbp = 0u;
|
||||
uint32_t sourceFbp = 0u;
|
||||
uint32_t width = 0u;
|
||||
uint32_t height = 0u;
|
||||
bool usedPreferred = false;
|
||||
bool hasFrame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> presentationLock(m_presentationMutex);
|
||||
m_hostPresentationFrame.swap(presentationGs.m_hostPresentationFrame);
|
||||
m_hostPresentationWidth = presentationGs.m_hostPresentationWidth;
|
||||
m_hostPresentationHeight = presentationGs.m_hostPresentationHeight;
|
||||
m_hostPresentationDisplayFbp = presentationGs.m_hostPresentationDisplayFbp;
|
||||
m_hostPresentationSourceFbp = presentationGs.m_hostPresentationSourceFbp;
|
||||
m_hostPresentationUsedPreferred = presentationGs.m_hostPresentationUsedPreferred;
|
||||
m_hasHostPresentationFrame = presentationGs.m_hasHostPresentationFrame;
|
||||
|
||||
displayFbp = m_hostPresentationDisplayFbp;
|
||||
sourceFbp = m_hostPresentationSourceFbp;
|
||||
width = m_hostPresentationWidth;
|
||||
height = m_hostPresentationHeight;
|
||||
usedPreferred = m_hostPresentationUsedPreferred;
|
||||
hasFrame = m_hasHostPresentationFrame;
|
||||
}
|
||||
|
||||
if (hasFrame)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
recordPresentDebugEventUnlocked(displayFbp, sourceFbp, width, height, usedPreferred);
|
||||
}
|
||||
}
|
||||
|
||||
void GS::latchHostPresentationFrameUnlocked()
|
||||
@@ -1249,7 +1347,7 @@ bool GS::copyLatchedHostPresentationFrame(std::vector<uint8_t> &outPixels,
|
||||
uint32_t *outSourceFbp,
|
||||
bool *outUsedPreferred) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
std::lock_guard<std::mutex> lock(m_presentationMutex);
|
||||
if (!m_hasHostPresentationFrame || m_hostPresentationFrame.empty())
|
||||
{
|
||||
outPixels.clear();
|
||||
@@ -1355,7 +1453,7 @@ void GS::processGIFPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
bool pre = ((tagLo >> 46) & 1) != 0;
|
||||
if (pre)
|
||||
{
|
||||
writeRegister(GS_REG_PRIM, (tagLo >> 47) & 0x7FF);
|
||||
writeRegisterUnlocked(GS_REG_PRIM, (tagLo >> 47) & 0x7FF);
|
||||
}
|
||||
|
||||
uint8_t regs[16];
|
||||
@@ -1385,7 +1483,7 @@ void GS::processGIFPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
{
|
||||
if (offset + 8 > sizeBytes)
|
||||
return;
|
||||
writeRegister(regs[r], loadLE64(data + offset));
|
||||
writeRegisterUnlocked(regs[r], loadLE64(data + offset));
|
||||
offset += 8;
|
||||
}
|
||||
}
|
||||
@@ -1420,7 +1518,7 @@ bool GS::processNativePackedGIFPacket(const uint8_t *data, uint32_t sizeBytes)
|
||||
|
||||
const bool pre = ((tag.lo >> 46u) & 1u) != 0u;
|
||||
if (pre)
|
||||
writeRegister(GS_REG_PRIM, (tag.lo >> 47u) & 0x7FFu);
|
||||
writeRegisterUnlocked(GS_REG_PRIM, (tag.lo >> 47u) & 0x7FFu);
|
||||
|
||||
uint32_t offset = tag.payloadOffset;
|
||||
for (uint32_t loop = 0u; loop < tag.nloop; ++loop)
|
||||
@@ -1451,13 +1549,23 @@ void GS::uploadImageNative(uint64_t bitbltbuf,
|
||||
uint32_t sizeBytes)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
uploadImageNativeUnlocked(bitbltbuf, trxpos, trxreg, trxdir, data, sizeBytes);
|
||||
}
|
||||
|
||||
void GS::uploadImageNativeUnlocked(uint64_t bitbltbuf,
|
||||
uint64_t trxpos,
|
||||
uint64_t trxreg,
|
||||
uint64_t trxdir,
|
||||
const uint8_t *data,
|
||||
uint32_t sizeBytes)
|
||||
{
|
||||
if (!data || sizeBytes == 0 || !m_vram)
|
||||
return;
|
||||
|
||||
writeRegister(GS_REG_BITBLTBUF, bitbltbuf);
|
||||
writeRegister(GS_REG_TRXPOS, trxpos);
|
||||
writeRegister(GS_REG_TRXREG, trxreg);
|
||||
writeRegister(GS_REG_TRXDIR, trxdir);
|
||||
writeRegisterUnlocked(GS_REG_BITBLTBUF, bitbltbuf);
|
||||
writeRegisterUnlocked(GS_REG_TRXPOS, trxpos);
|
||||
writeRegisterUnlocked(GS_REG_TRXREG, trxreg);
|
||||
writeRegisterUnlocked(GS_REG_TRXDIR, trxdir);
|
||||
processImageData(data, sizeBytes);
|
||||
++m_nativeImageUploadCount;
|
||||
}
|
||||
@@ -1528,7 +1636,7 @@ bool GS::tryProcessNativeImageUploadPacket(const uint8_t *data, uint32_t sizeByt
|
||||
if (offset + imageBytes != sizeBytes)
|
||||
return false;
|
||||
|
||||
uploadImageNative(regs[0], regs[1], regs[2], regs[3], data + offset, imageBytes);
|
||||
uploadImageNativeUnlocked(regs[0], regs[1], regs[2], regs[3], data + offset, imageBytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1537,7 +1645,7 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi)
|
||||
switch (regDesc)
|
||||
{
|
||||
case 0x00:
|
||||
writeRegister(GS_REG_PRIM, lo & 0x7FF);
|
||||
writeRegisterUnlocked(GS_REG_PRIM, lo & 0x7FF);
|
||||
break;
|
||||
case 0x01:
|
||||
m_curR = static_cast<uint8_t>(lo & 0xFF);
|
||||
@@ -1705,13 +1813,13 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi)
|
||||
case 0x0E:
|
||||
{
|
||||
uint8_t addr = static_cast<uint8_t>(hi & 0xFF);
|
||||
writeRegister(addr, lo);
|
||||
writeRegisterUnlocked(addr, lo);
|
||||
break;
|
||||
}
|
||||
case 0x0F:
|
||||
break;
|
||||
default:
|
||||
writeRegister(regDesc, lo);
|
||||
writeRegisterUnlocked(regDesc, lo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1719,6 +1827,11 @@ void GS::writeRegisterPacked(uint8_t regDesc, uint64_t lo, uint64_t hi)
|
||||
void GS::writeRegister(uint8_t regAddr, uint64_t value)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(m_stateMutex);
|
||||
writeRegisterUnlocked(regAddr, value);
|
||||
}
|
||||
|
||||
void GS::writeRegisterUnlocked(uint8_t regAddr, uint64_t value)
|
||||
{
|
||||
const bool interestingReg =
|
||||
regAddr == GS_REG_PRIM ||
|
||||
regAddr == GS_REG_RGBAQ ||
|
||||
|
||||
@@ -622,28 +622,38 @@ bool PS2Runtime::syncCoreSubsystems()
|
||||
m_memory.setGifArbiter(&m_gifArbiter);
|
||||
m_memory.setVu1MscalCallback([this](uint32_t startPC, uint32_t top, uint32_t itop)
|
||||
{
|
||||
R5900Context *cpuContext = m_eeScheduler ? m_eeScheduler->currentContext() : nullptr;
|
||||
if (!cpuContext)
|
||||
{
|
||||
cpuContext = &m_cpuContext;
|
||||
}
|
||||
m_vu1.state().dBitEnabled =
|
||||
(m_cpuContext.vu0_fbrst & (1u << 10)) != 0u;
|
||||
(cpuContext->vu0_fbrst & (1u << 10)) != 0u;
|
||||
m_vu1.state().tBitEnabled =
|
||||
(m_cpuContext.vu0_fbrst & (1u << 11)) != 0u;
|
||||
(cpuContext->vu0_fbrst & (1u << 11)) != 0u;
|
||||
m_vu1.execute(m_memory.getVU1Code(), PS2_VU1_CODE_SIZE,
|
||||
m_memory.getVU1Data(), PS2_VU1_DATA_SIZE,
|
||||
m_gs, &m_memory, startPC, top, itop, 65536);
|
||||
m_cpuContext.vu0_vpu_stat =
|
||||
(m_cpuContext.vu0_vpu_stat & ~0x0600u) |
|
||||
cpuContext->vu0_vpu_stat =
|
||||
(cpuContext->vu0_vpu_stat & ~0x0600u) |
|
||||
(m_vu1.state().stoppedByD ? 0x0200u : 0u) |
|
||||
(m_vu1.state().stoppedByT ? 0x0400u : 0u); });
|
||||
m_memory.setVu1MscntCallback([this](uint32_t top, uint32_t itop)
|
||||
{
|
||||
R5900Context *cpuContext = m_eeScheduler ? m_eeScheduler->currentContext() : nullptr;
|
||||
if (!cpuContext)
|
||||
{
|
||||
cpuContext = &m_cpuContext;
|
||||
}
|
||||
m_vu1.state().dBitEnabled =
|
||||
(m_cpuContext.vu0_fbrst & (1u << 10)) != 0u;
|
||||
(cpuContext->vu0_fbrst & (1u << 10)) != 0u;
|
||||
m_vu1.state().tBitEnabled =
|
||||
(m_cpuContext.vu0_fbrst & (1u << 11)) != 0u;
|
||||
(cpuContext->vu0_fbrst & (1u << 11)) != 0u;
|
||||
m_vu1.resume(m_memory.getVU1Code(), PS2_VU1_CODE_SIZE,
|
||||
m_memory.getVU1Data(), PS2_VU1_DATA_SIZE,
|
||||
m_gs, &m_memory, top, itop, 65536);
|
||||
m_cpuContext.vu0_vpu_stat =
|
||||
(m_cpuContext.vu0_vpu_stat & ~0x0600u) |
|
||||
cpuContext->vu0_vpu_stat =
|
||||
(cpuContext->vu0_vpu_stat & ~0x0600u) |
|
||||
(m_vu1.state().stoppedByD ? 0x0200u : 0u) |
|
||||
(m_vu1.state().stoppedByT ? 0x0400u : 0u); });
|
||||
resetIop();
|
||||
@@ -2160,6 +2170,18 @@ bool PS2Runtime::eeCheckpointDue() const noexcept
|
||||
return m_eeScheduler->checkpointDue();
|
||||
}
|
||||
|
||||
[[noreturn]] void PS2Runtime::eeWaitVSyncTicks(uint32_t ticks, uint32_t resumePc)
|
||||
{
|
||||
const uint64_t currentTick = m_eeScheduler->currentVSyncTick();
|
||||
const uint64_t waitTicks = std::max<uint64_t>(1u, ticks);
|
||||
m_eeScheduler->waitVSync(currentTick + waitTicks - 1u,
|
||||
0,
|
||||
[resumePc](R5900Context &context)
|
||||
{
|
||||
context.pc = resumePc;
|
||||
});
|
||||
}
|
||||
|
||||
void PS2Runtime::addEeExitHandler(int threadId, uint32_t function, uint32_t argument)
|
||||
{
|
||||
std::lock_guard lock(m_eeKernelStateMutex);
|
||||
|
||||
Reference in New Issue
Block a user