From 7cffacb83966d0e95bc97fb2c1455e4a89028358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gerdau?= Date: Wed, 23 Mar 2022 13:25:56 +0100 Subject: [PATCH 1/2] Implement WaitNonVblank --- Source/iop/Iop_Vblank.cpp | 16 ++++++++++++++++ Source/iop/Iop_Vblank.h | 1 + 2 files changed, 17 insertions(+) diff --git a/Source/iop/Iop_Vblank.cpp b/Source/iop/Iop_Vblank.cpp index a3829fcc5c..93c6acae41 100644 --- a/Source/iop/Iop_Vblank.cpp +++ b/Source/iop/Iop_Vblank.cpp @@ -9,6 +9,7 @@ using namespace Iop; #define FUNCTION_WAITVBLANKSTART "WaitVblankStart" #define FUNCTION_WAITVBLANKEND "WaitVblankEnd" #define FUNCTION_WAITVBLANK "WaitVblank" +#define FUNCTION_WAITNONVBLANK "WaitNonVblank" #define FUNCTION_REGISTERVBLANKHANDLER "RegisterVblankHandler" #define FUNCTION_RELEASEVBLANKHANDLER "ReleaseVblankHandler" @@ -35,6 +36,9 @@ std::string CVblank::GetFunctionName(unsigned int functionId) const case 6: return FUNCTION_WAITVBLANK; break; + case 7: + return FUNCTION_WAITNONVBLANK; + break; case 8: return FUNCTION_REGISTERVBLANKHANDLER; break; @@ -60,6 +64,9 @@ void CVblank::Invoke(CMIPS& context, unsigned int functionId) case 6: context.m_State.nGPR[CMIPS::V0].nD0 = WaitVblank(); break; + case 7: + context.m_State.nGPR[CMIPS::V0].nD0 = WaitNonVblank(); + break; case 8: context.m_State.nGPR[CMIPS::V0].nD0 = RegisterVblankHandler( context.m_State.nGPR[CMIPS::A0].nV0, @@ -106,6 +113,15 @@ int32 CVblank::WaitVblank() return 0; } +int32 CVblank::WaitNonVblank() +{ +#ifdef _DEBUG + CLog::GetInstance().Print(LOG_NAME, FUNCTION_WAITNONVBLANK "();\r\n"); +#endif + m_bios.SleepThreadTillVBlankEnd(); + return 0; +} + int32 CVblank::RegisterVblankHandler(uint32 startEnd, uint32 priority, uint32 handlerPtr, uint32 handlerParam) { #ifdef _DEBUG diff --git a/Source/iop/Iop_Vblank.h b/Source/iop/Iop_Vblank.h index 27af575534..7b359f540e 100644 --- a/Source/iop/Iop_Vblank.h +++ b/Source/iop/Iop_Vblank.h @@ -20,6 +20,7 @@ namespace Iop int32 WaitVblankStart(); int32 WaitVblankEnd(); int32 WaitVblank(); + int32 WaitNonVblank(); int32 RegisterVblankHandler(uint32, uint32, uint32, uint32); int32 ReleaseVblankHandler(uint32, uint32); From 8b40b30e3791d3f5f8d02914ed0749a481f7faa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gerdau?= Date: Wed, 23 Mar 2022 13:39:07 +0100 Subject: [PATCH 2/2] Improve VBlank timing Some vblank wait methods can skip sleeping, if they are called inside or outside a vblank (according to their function). --- Source/iop/IopBios.cpp | 29 +++++++++++++++++++++++++++++ Source/iop/IopBios.h | 3 +++ Source/iop/Iop_Vblank.cpp | 5 ++--- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/Source/iop/IopBios.cpp b/Source/iop/IopBios.cpp index 21daf0cee9..e290a90152 100644 --- a/Source/iop/IopBios.cpp +++ b/Source/iop/IopBios.cpp @@ -51,6 +51,7 @@ #define BIOS_THREAD_LINK_HEAD_BASE (CIopBios::CONTROL_BLOCK_START + 0x0000) #define BIOS_CURRENT_THREAD_ID_BASE (CIopBios::CONTROL_BLOCK_START + 0x0008) #define BIOS_CURRENT_TIME_BASE (CIopBios::CONTROL_BLOCK_START + 0x0010) +#define BIOS_IN_VBLANK_BASE (CIopBios::CONTROL_BLOCK_START + 0x0014) #define BIOS_MODULESTARTREQUEST_HEAD_BASE (CIopBios::CONTROL_BLOCK_START + 0x0018) #define BIOS_MODULESTARTREQUEST_FREE_BASE (CIopBios::CONTROL_BLOCK_START + 0x0020) #define BIOS_HANDLERS_BASE (CIopBios::CONTROL_BLOCK_START + 0x0100) @@ -116,6 +117,7 @@ CIopBios::CIopBios(CMIPS& cpu, uint8* ram, uint32 ramSize, uint8* spr) , m_vpls(reinterpret_cast(&m_ram[BIOS_VPL_BASE]), 1, MAX_VPL) , m_loadedModules(reinterpret_cast(&m_ram[BIOS_LOADEDMODULE_BASE]), 1, MAX_LOADEDMODULE) , m_currentThreadId(reinterpret_cast(m_ram + BIOS_CURRENT_THREAD_ID_BASE)) + , m_inVBlank(reinterpret_cast(m_ram + BIOS_IN_VBLANK_BASE)) { static_assert(BIOS_CALCULATED_END <= CIopBios::CONTROL_BLOCK_END, "Control block size is too small"); static_assert(BIOS_SYSTEM_INTRHANDLER_TABLE_BASE > CIopBios::CONTROL_BLOCK_START, "Intr handler table is outside reserved block"); @@ -146,6 +148,7 @@ void CIopBios::Reset(const Iop::SifManPtr& sifMan) CurrentTime() = 0xBE00000; ThreadLinkHead() = 0; m_currentThreadId = -1; + m_inVBlank = false; m_cpu.m_State.nCOP0[CCOP_SCU::STATUS] |= CMIPS::STATUS_IE; @@ -1692,6 +1695,30 @@ void CIopBios::SleepThreadTillVBlankEnd() m_rescheduleNeeded = true; } +void CIopBios::SleepThreadTillVBlank() +{ + if(m_inVBlank) + { + return; + } + THREAD* thread = GetThread(m_currentThreadId); + thread->status = THREAD_STATUS_WAIT_VBLANK_START; + UnlinkThread(thread->id); + m_rescheduleNeeded = true; +} + +void CIopBios::SleepThreadTillNonVBlank() +{ + if(!m_inVBlank) + { + return; + } + THREAD* thread = GetThread(m_currentThreadId); + thread->status = THREAD_STATUS_WAIT_VBLANK_END; + UnlinkThread(thread->id); + m_rescheduleNeeded = true; +} + void CIopBios::LoadThreadContext(uint32 threadId) { THREAD* thread = GetThread(threadId); @@ -1849,6 +1876,7 @@ void CIopBios::CountTicks(uint32 ticks) void CIopBios::NotifyVBlankStart() { + m_inVBlank = true; for(auto thread : m_threads) { if(!thread) continue; @@ -1862,6 +1890,7 @@ void CIopBios::NotifyVBlankStart() void CIopBios::NotifyVBlankEnd() { + m_inVBlank = false; for(auto thread : m_threads) { if(!thread) continue; diff --git a/Source/iop/IopBios.h b/Source/iop/IopBios.h index ae0588fe07..93aa213fef 100644 --- a/Source/iop/IopBios.h +++ b/Source/iop/IopBios.h @@ -239,6 +239,8 @@ class CIopBios : public Iop::CBiosBase int32 FindVblankHandlerByLineAndPtr(uint32 startEnd, uint32 handlerPtr); void SleepThreadTillVBlankStart(); void SleepThreadTillVBlankEnd(); + void SleepThreadTillVBlank(); + void SleepThreadTillNonVBlank(); uint32 CreateSemaphore(uint32, uint32, uint32, uint32); uint32 DeleteSemaphore(uint32); @@ -662,6 +664,7 @@ class CIopBios : public Iop::CBiosBase IopModuleMapType m_modules; OsVariableWrapper m_currentThreadId; + OsVariableWrapper m_inVBlank; #ifdef DEBUGGER_INCLUDED BiosDebugModuleInfoArray m_moduleTags; diff --git a/Source/iop/Iop_Vblank.cpp b/Source/iop/Iop_Vblank.cpp index 93c6acae41..8b37fd3372 100644 --- a/Source/iop/Iop_Vblank.cpp +++ b/Source/iop/Iop_Vblank.cpp @@ -108,8 +108,7 @@ int32 CVblank::WaitVblank() #ifdef _DEBUG CLog::GetInstance().Print(LOG_NAME, FUNCTION_WAITVBLANK "();\r\n"); #endif - //TODO: Skip waiting if we're already in Vblank - m_bios.SleepThreadTillVBlankStart(); + m_bios.SleepThreadTillVBlank(); return 0; } @@ -118,7 +117,7 @@ int32 CVblank::WaitNonVblank() #ifdef _DEBUG CLog::GetInstance().Print(LOG_NAME, FUNCTION_WAITNONVBLANK "();\r\n"); #endif - m_bios.SleepThreadTillVBlankEnd(); + m_bios.SleepThreadTillNonVBlank(); return 0; }