camera: make timings more realistic (fixes rolling in some games)

This commit is contained in:
Arisotura
2025-07-05 00:14:11 +02:00
parent f352cf612a
commit 16d1464d19
2 changed files with 27 additions and 10 deletions

View File

@ -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;
}

View File

@ -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;
};