From 16d1464d19ed308e4066280090128bd32dcf18ac Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 5 Jul 2025 00:14:11 +0200 Subject: [PATCH] camera: make timings more realistic (fixes rolling in some games) --- src/DSi_Camera.cpp | 33 ++++++++++++++++++++++++--------- src/DSi_Camera.h | 4 +++- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index a5248af3..38251fd6 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -34,8 +34,11 @@ using Platform::LogLevel; // namely, how long cameras take to process frames // camera IRQ is fired at roughly 15FPS with default config -const u32 DSi_CamModule::kIRQInterval = 1120000; // ~30 FPS -const u32 DSi_CamModule::kTransferStart = 60000; +// camera IRQ marks camera VBlank +// each scanline takes roughly 3173 cycles +const u32 DSi_CamModule::kIRQInterval = 2234248; // ~15 FPS +const u32 DSi_CamModule::kScanlineTime = 3173; +const u32 DSi_CamModule::kTransferStart = DSi_CamModule::kIRQInterval - (DSi_CamModule::kScanlineTime * 480); DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi) @@ -127,11 +130,11 @@ void DSi_CamModule::TransferScanline(u32 line) int maxlen = 512 - BufferWritePos; u32 tmpbuf[512]; - int datalen = CurCamera->TransferScanline(tmpbuf, 512); + int lines_next; + int datalen = CurCamera->TransferScanline(tmpbuf, 512, lines_next); u32 numscan; - // TODO: must be tweaked such that each block has enough time to transfer - u32 delay = datalen*4 + 16; + u32 delay = lines_next * kScanlineTime; int copystart = 0; int copylen = datalen; @@ -438,6 +441,7 @@ void DSi_Camera::Reset() // default state is preview mode (checkme) MCURegs[0x2104] = 3; + InternalY = 0; TransferY = 0; memset(FrameBuffer, 0, (640*480/2)*sizeof(u32)); } @@ -458,6 +462,7 @@ bool DSi_Camera::IsActivated() const void DSi_Camera::StartTransfer() { + InternalY = 0; TransferY = 0; u8 state = MCURegs[0x2104]; @@ -491,9 +496,11 @@ bool DSi_Camera::TransferDone() const return TransferY >= FrameHeight; } -int DSi_Camera::TransferScanline(u32* buffer, int maxlen) +int DSi_Camera::TransferScanline(u32* buffer, int maxlen, int& nlines) { - if (TransferY >= FrameHeight) + nlines = 0; + + if ((TransferY >= FrameHeight) || (InternalY >= 480)) return 0; if (FrameWidth > 640 || FrameHeight > 480 || @@ -509,7 +516,7 @@ int DSi_Camera::TransferScanline(u32* buffer, int maxlen) // TODO: non-YUV pixel formats and all int retlen = FrameWidth >> 1; - int sy = (TransferY * 480) / FrameHeight; + int sy = InternalY; if (FrameReadMode & (1<<1)) sy = 479 - sy; @@ -538,7 +545,15 @@ int DSi_Camera::TransferScanline(u32* buffer, int maxlen) } } - TransferY++; + // determine how many scanlines we're skipping until the next scanline + int oldy = TransferY; + do + { + InternalY++; + TransferY = (InternalY * FrameHeight) / 480; + nlines++; + } + while ((TransferY == oldy) && (InternalY < 480)); return retlen; } diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index 5a626f56..61934bc8 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -44,7 +44,7 @@ public: bool TransferDone() const; // lengths in words - int TransferScanline(u32* buffer, int maxlen); + int TransferScanline(u32* buffer, int maxlen, int& nlines); void Acquire() override; u8 Read(bool last) override; @@ -77,6 +77,7 @@ private: u16 FrameWidth, FrameHeight; u16 FrameReadMode, FrameFormat; + int InternalY; int TransferY; u32 FrameBuffer[640*480/2]; // YUYV framebuffer, two pixels per word }; @@ -124,6 +125,7 @@ private: DSi_Camera* CurCamera; static const u32 kIRQInterval; + static const u32 kScanlineTime; static const u32 kTransferStart; };