diff --git a/src/common/xbe/Xbe.cpp b/src/common/xbe/Xbe.cpp index 36b93ab5c..3f9c07fe3 100644 --- a/src/common/xbe/Xbe.cpp +++ b/src/common/xbe/Xbe.cpp @@ -72,7 +72,7 @@ Xbe::Xbe(const char *x_szFilename) // This is necessary because CxbxInitWindow internally calls g_AffinityPolicy->SetAffinityOther. If we are launched directly from the command line and the dashboard // cannot be opened, we will crash below because g_AffinityPolicy will be empty g_AffinityPolicy = AffinityPolicy::InitPolicy(); - CxbxInitWindow(false); + CxbxInitWindow(); ULONG FatalErrorCode = FATAL_ERROR_XBE_DASH_GENERIC; diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 9237c8197..04cb005c5 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -228,7 +228,6 @@ static xbox::dword_xt *g_Xbox_D3DDevice; // TODO: This should b // Static Function(s) static DWORD WINAPI EmuRenderWindow(LPVOID); static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -static xbox::void_xt NTAPI EmuUpdateTickCount(xbox::PVOID Arg); static inline void EmuVerifyResourceIsRegistered(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTextureStage, DWORD dwSize); static void UpdateCurrentMSpFAndFPS(); // Used for benchmarking/fps count static void CxbxImpl_SetRenderTarget(xbox::X_D3DSurface *pRenderTarget, xbox::X_D3DSurface *pNewZStencil); @@ -628,25 +627,13 @@ const char *D3DErrorString(HRESULT hResult) return buffer; } -void CxbxInitWindow(bool bFullInit) +void CxbxInitWindow() { g_EmuShared->GetVideoSettings(&g_XBVideo); if(g_XBVideo.bFullScreen) CxbxKrnl_hEmuParent = NULL; - // create timing thread - if (bFullInit && !bLLE_GPU) - { - xbox::HANDLE hThread; - xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, EmuUpdateTickCount, xbox::zeroptr, FALSE); - // We set the priority of this thread a bit higher, to assure reliable timing : - auto nativeHandle = GetNativeHandle(hThread); - assert(nativeHandle); - SetThreadPriority(*nativeHandle, THREAD_PRIORITY_ABOVE_NORMAL); - g_AffinityPolicy->SetAffinityOther(*nativeHandle); - } - /* TODO : Port this Dxbx code : // create vblank handling thread { @@ -1925,47 +1912,33 @@ std::chrono::steady_clock::time_point GetNextVBlankTime() return steady_clock::now() + duration_cast(ms); } -// timing thread procedure -static xbox::void_xt NTAPI EmuUpdateTickCount(xbox::PVOID Arg) +void hle_vblank() { - CxbxSetThreadName("Cxbx Timing Thread"); + // Note: This whole code block can be removed once NV2A interrupts are implemented + // And Both Swap and Present can be ran unpatched + // Once that is in place, MiniPort + Direct3D will handle this on it's own! + // Increment the VBlank Counter and Wake all threads there were waiting for the VBlank to occur + std::unique_lock lk(g_VBConditionMutex); + g_Xbox_VBlankData.VBlank++; + g_VBConditionVariable.notify_all(); - EmuLog(LOG_LEVEL::DEBUG, "Timing thread is running."); + // TODO: Fixme. This may not be right... + g_Xbox_SwapData.SwapVBlank = 1; - auto nextVBlankTime = GetNextVBlankTime(); - - while(true) - { - // Wait for VBlank - // Note: This whole code block can be removed once NV2A interrupts are implemented - // And Both Swap and Present can be ran unpatched - // Once that is in place, MiniPort + Direct3D will handle this on it's own! - SleepPrecise(nextVBlankTime); - nextVBlankTime = GetNextVBlankTime(); - - // Increment the VBlank Counter and Wake all threads there were waiting for the VBlank to occur - std::unique_lock lk(g_VBConditionMutex); - g_Xbox_VBlankData.VBlank++; - g_VBConditionVariable.notify_all(); - - // TODO: Fixme. This may not be right... - g_Xbox_SwapData.SwapVBlank = 1; - - if(g_pXbox_VerticalBlankCallback != xbox::zeroptr) - { - g_pXbox_VerticalBlankCallback(&g_Xbox_VBlankData); - } - - g_Xbox_VBlankData.Swap = 0; - - // TODO: This can't be accurate... - g_Xbox_SwapData.TimeUntilSwapVBlank = 0; - - // TODO: Recalculate this for PAL version if necessary. - // Also, we should check the D3DPRESENT_INTERVAL value for accurracy. - // g_Xbox_SwapData.TimeBetweenSwapVBlanks = 1/60; - g_Xbox_SwapData.TimeBetweenSwapVBlanks = 0; + if (g_pXbox_VerticalBlankCallback != xbox::zeroptr) + { + g_pXbox_VerticalBlankCallback(&g_Xbox_VBlankData); } + + g_Xbox_VBlankData.Swap = 0; + + // TODO: This can't be accurate... + g_Xbox_SwapData.TimeUntilSwapVBlank = 0; + + // TODO: Recalculate this for PAL version if necessary. + // Also, we should check the D3DPRESENT_INTERVAL value for accurracy. + // g_Xbox_SwapData.TimeBetweenSwapVBlanks = 1/60; + g_Xbox_SwapData.TimeBetweenSwapVBlanks = 0; } void UpdateDepthStencilFlags(IDirect3DSurface *pDepthStencilSurface) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.h b/src/core/hle/D3D8/Direct3D9/Direct3D9.h index aa13b1dd4..e508ff39a 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.h +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.h @@ -40,7 +40,7 @@ void LookupTrampolinesD3D(); // initialize render window -extern void CxbxInitWindow(bool bFullInit); +extern void CxbxInitWindow(); void CxbxUpdateNativeD3DResources(); diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 8ba656e01..bee2631cc 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -1344,7 +1344,7 @@ static void CxbxrKrnlInitHacks() // initialize graphics EmuLogInit(LOG_LEVEL::DEBUG, "Initializing render window."); - CxbxInitWindow(true); + CxbxInitWindow(); // Now process the boot flags to see if there are any special conditions to handle if (BootFlags & BOOT_EJECT_PENDING) {} // TODO diff --git a/src/devices/video/nv2a.cpp b/src/devices/video/nv2a.cpp index 6c0e62aa0..0f1b1d645 100644 --- a/src/devices/video/nv2a.cpp +++ b/src/devices/video/nv2a.cpp @@ -51,6 +51,7 @@ #include "core\kernel\init\CxbxKrnl.h" // For XBOX_MEMORY_SIZE, DWORD, etc #include "core\kernel\support\Emu.h" +#include "core\kernel\support\NativeHandle.h" #include "core\kernel\exports\EmuKrnl.h" #include #include @@ -58,6 +59,7 @@ #include "core\hle\Intercept.hpp" #include "common/win32/Threads.h" #include "Logging.h" +#include "Timer.h" #include "vga.h" #include "nv2a.h" // For NV2AState @@ -319,8 +321,8 @@ const NV2ABlockInfo* EmuNV2A_Block(xbox::addr_xt addr) // HACK: Until we implement VGA/proper interrupt generation // we simulate VBLANK by calling the interrupt at 60Hz -std::thread vblank_thread; extern std::chrono::steady_clock::time_point GetNextVBlankTime(); +extern void hle_vblank(); void _check_gl_reset() { @@ -1097,25 +1099,37 @@ void NV2ADevice::UpdateHostDisplay(NV2AState *d) } // TODO: Fix this properly -static void nv2a_vblank_thread(NV2AState *d) +template +static xbox::void_xt NTAPI nv2a_vblank_thread(xbox::PVOID arg) { - g_AffinityPolicy->SetAffinityOther(); - CxbxSetThreadName("Cxbx NV2A VBLANK"); + NV2AState *d = static_cast(arg); + + if constexpr (should_update_hle) { + CxbxSetThreadName("Cxbxr NV2A and HLE VBLANK"); + } + else { + g_AffinityPolicy->SetAffinityOther(); + CxbxSetThreadName("Cxbxr NV2A VBLANK"); + } + auto nextVBlankTime = GetNextVBlankTime(); - while (!d->exiting) { - // Handle VBlank - if (std::chrono::steady_clock::now() > nextVBlankTime) { - d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK; - update_irq(d); - nextVBlankTime = GetNextVBlankTime(); + while (!d->exiting) [[unlikely]] { - // TODO: We should swap here for the purposes of supporting overlays + direct framebuffer access - // But it causes crashes on AMD hardware for reasons currently unknown... - //NV2ADevice::UpdateHostDisplay(d); + // Wait for VBlank + SleepPrecise(nextVBlankTime); + nextVBlankTime = GetNextVBlankTime(); + + d->pcrtc.pending_interrupts |= NV_PCRTC_INTR_0_VBLANK; + update_irq(d); + + // TODO: We should swap here for the purposes of supporting overlays + direct framebuffer access + // But it causes crashes on AMD hardware for reasons currently unknown... + //NV2ADevice::UpdateHostDisplay(d); + + if constexpr (should_update_hle) { + hle_vblank(); } - - std::this_thread::sleep_for(std::chrono::milliseconds(1)); } } @@ -1202,7 +1216,20 @@ void NV2ADevice::Init() pvideo_init(d); } - vblank_thread = std::thread(nv2a_vblank_thread, d); + + if (bLLE_GPU) { + xbox::HANDLE hThread; + xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, nv2a_vblank_thread, d, FALSE); + d->vblank_thread = *GetNativeHandle(hThread); + } + else { + xbox::HANDLE hThread; + xbox::PsCreateSystemThread(&hThread, xbox::zeroptr, nv2a_vblank_thread, d, FALSE); + // We set the priority of this thread a bit higher, to assure reliable timing + d->vblank_thread = *GetNativeHandle(hThread); + SetThreadPriority(d->vblank_thread, THREAD_PRIORITY_ABOVE_NORMAL); + g_AffinityPolicy->SetAffinityOther(d->vblank_thread); + } qemu_mutex_init(&d->pfifo.pfifo_lock); qemu_cond_init(&d->pfifo.puller_cond); @@ -1227,9 +1254,9 @@ void NV2ADevice::Reset() qemu_cond_broadcast(&d->pfifo.pusher_cond); d->pfifo.puller_thread.join(); d->pfifo.pusher_thread.join(); - qemu_mutex_destroy(&d->pfifo.pfifo_lock); // Cbxbx addition + qemu_mutex_destroy(&d->pfifo.pfifo_lock); // Cxbxr addition if (d->pgraph.opengl_enabled) { - vblank_thread.join(); + WaitForSingleObject(d->vblank_thread, INFINITE); pvideo_destroy(d); } diff --git a/src/devices/video/nv2a_int.h b/src/devices/video/nv2a_int.h index 22630b146..72c8276c3 100644 --- a/src/devices/video/nv2a_int.h +++ b/src/devices/video/nv2a_int.h @@ -357,6 +357,7 @@ typedef struct OverlayState { } OverlayState; typedef struct NV2AState { + HANDLE vblank_thread; // PCIDevice dev; // qemu_irq irq; bool exiting;