diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index d691e81..94be058 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -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 { diff --git a/ps2xRuntime/include/runtime/ee_scheduler.h b/ps2xRuntime/include/runtime/ee_scheduler.h index 03ac89a..454cd94 100644 --- a/ps2xRuntime/include/runtime/ee_scheduler.h +++ b/ps2xRuntime/include/runtime/ee_scheduler.h @@ -409,6 +409,7 @@ private: std::atomic m_guestExecuting{false}; std::atomic m_stopRequested{false}; std::atomic m_checkpointPending{false}; + uint32_t m_debugPublishCountdown = 0u; mutable std::mutex m_eventMutex; std::condition_variable m_eventCv; diff --git a/ps2xRuntime/include/runtime/ps2_gs_gpu.h b/ps2xRuntime/include/runtime/ps2_gs_gpu.h index bb0b140..d968326 100644 --- a/ps2xRuntime/include/runtime/ps2_gs_gpu.h +++ b/ps2xRuntime/include/runtime/ps2_gs_gpu.h @@ -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{}; diff --git a/ps2xRuntime/src/lib/Kernel/EeScheduler.cpp b/ps2xRuntime/src/lib/Kernel/EeScheduler.cpp index 551bc95..3adde69 100644 --- a/ps2xRuntime/src/lib/Kernel/EeScheduler.cpp +++ b/ps2xRuntime/src/lib/Kernel/EeScheduler.cpp @@ -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 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); diff --git a/ps2xRuntime/src/lib/ps2_gs_gpu.cpp b/ps2xRuntime/src/lib/ps2_gs_gpu.cpp index d9bc648..d444ad8 100644 --- a/ps2xRuntime/src/lib/ps2_gs_gpu.cpp +++ b/ps2xRuntime/src/lib/ps2_gs_gpu.cpp @@ -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 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 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 lock(m_stateMutex); - latchHostPresentationFrameUnlocked(); + thread_local std::vector 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 lock(m_stateMutex); + if (!m_privRegs || !m_vram || m_vramSize == 0u) + { + std::lock_guard 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 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 lock(m_stateMutex); + recordPresentDebugEventUnlocked(displayFbp, sourceFbp, width, height, usedPreferred); + } } void GS::latchHostPresentationFrameUnlocked() @@ -1249,7 +1347,7 @@ bool GS::copyLatchedHostPresentationFrame(std::vector &outPixels, uint32_t *outSourceFbp, bool *outUsedPreferred) const { - std::lock_guard lock(m_stateMutex); + std::lock_guard 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 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(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(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 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 || diff --git a/ps2xRuntime/src/lib/ps2_runtime.cpp b/ps2xRuntime/src/lib/ps2_runtime.cpp index e59a847..ae0b2b0 100644 --- a/ps2xRuntime/src/lib/ps2_runtime.cpp +++ b/ps2xRuntime/src/lib/ps2_runtime.cpp @@ -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(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);