feat: cheap copy from host

feat: small perf o vsync tick
This commit is contained in:
Ran-j
2026-08-05 19:03:03 -03:00
parent c09e90aa93
commit 3e5fb2b3b4
6 changed files with 192 additions and 37 deletions
+1
View File
@@ -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;
+8
View File
@@ -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{};
+12 -2
View File
@@ -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);
+140 -27
View File
@@ -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 ||
+30 -8
View File
@@ -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);