diff --git a/resource/Cxbx.rc b/resource/Cxbx.rc index 2c39c4356..d7558cc52 100644 --- a/resource/Cxbx.rc +++ b/resource/Cxbx.rc @@ -617,7 +617,6 @@ BEGIN BEGIN MENUITEM "Run Xbox threads on all cores", ID_HACKS_RUNXBOXTHREADSONALLCORES,MFT_STRING,MFS_ENABLED MENUITEM "Render directly to Host Backbuffer", ID_HACKS_RENDERDIRECTLYTOHOSTBACKBUFFER,MFT_STRING,MFS_ENABLED - MENUITEM "Uncap Framerate", ID_HACKS_UNCAPFRAMERATE,MFT_STRING,MFS_ENABLED END MENUITEM "Disable Pixel Shaders", ID_HACKS_DISABLEPIXELSHADERS,MFT_STRING,MFS_ENABLED MENUITEM "Skip rdtsc patching", ID_HACKS_SKIPRDTSCPATCHING,MFT_STRING,MFS_ENABLED diff --git a/src/common/Settings.cpp b/src/common/Settings.cpp index 43bc7c203..663c428f3 100644 --- a/src/common/Settings.cpp +++ b/src/common/Settings.cpp @@ -118,7 +118,6 @@ static struct { static const char* section_hack = "hack"; static struct { const char* DisablePixelShaders = "DisablePixelShaders"; - const char* UncapFramerate = "UncapFramerate"; const char* UseAllCores = "UseAllCores"; const char* SkipRdtscPatching = "SkipRdtscPatching"; const char* ScaleViewPort = "ScaleViewPort"; @@ -376,7 +375,6 @@ bool Settings::LoadConfig() // ==== Hack Begin ========== m_hacks.DisablePixelShaders = m_si.GetBoolValue(section_hack, sect_hack_keys.DisablePixelShaders, /*Default=*/false); - m_hacks.UncapFramerate = m_si.GetBoolValue(section_hack, sect_hack_keys.UncapFramerate, /*Default=*/false); m_hacks.UseAllCores = m_si.GetBoolValue(section_hack, sect_hack_keys.UseAllCores, /*Default=*/false); m_hacks.SkipRdtscPatching = m_si.GetBoolValue(section_hack, sect_hack_keys.SkipRdtscPatching, /*Default=*/false); m_hacks.ScaleViewport = m_si.GetBoolValue(section_hack, sect_hack_keys.ScaleViewPort, /*Default=*/false); @@ -636,7 +634,6 @@ bool Settings::Save(std::string file_path) // ==== Hack Begin ========== m_si.SetBoolValue(section_hack, sect_hack_keys.DisablePixelShaders, m_hacks.DisablePixelShaders, nullptr, true); - m_si.SetBoolValue(section_hack, sect_hack_keys.UncapFramerate, m_hacks.UncapFramerate, nullptr, true); m_si.SetBoolValue(section_hack, sect_hack_keys.UseAllCores, m_hacks.UseAllCores, nullptr, true); m_si.SetBoolValue(section_hack, sect_hack_keys.SkipRdtscPatching, m_hacks.SkipRdtscPatching, nullptr, true); m_si.SetBoolValue(section_hack, sect_hack_keys.ScaleViewPort, m_hacks.ScaleViewport, nullptr, true); diff --git a/src/common/Settings.hpp b/src/common/Settings.hpp index d168e129f..29c4a9fdd 100644 --- a/src/common/Settings.hpp +++ b/src/common/Settings.hpp @@ -204,10 +204,13 @@ public: unsigned int XboxPortMapHostPort[XBCTRL_MAX_GAMEPAD_PORTS] = { 0, 1, 2, 3 }; } m_controller_port; - // Hack settings + // Hack settings + // NOTE: When removing fields, replace them with place-holders + // The size and order of this structure should *not* be allowed to change + // TODO: Fix IPC/Shared Memory so this isn't necessary struct s_hack { - bool DisablePixelShaders; - bool UncapFramerate; + bool DisablePixelShaders; + bool Reserved2; bool UseAllCores; bool SkipRdtscPatching; bool ScaleViewport; diff --git a/src/common/win32/EmuShared.h b/src/common/win32/EmuShared.h index e41c75781..4bfa8b1c7 100644 --- a/src/common/win32/EmuShared.h +++ b/src/common/win32/EmuShared.h @@ -143,8 +143,6 @@ class EmuShared : public Mutex void GetDisablePixelShaders(int* value) { Lock(); *value = m_hacks.DisablePixelShaders; Unlock(); } void SetDisablePixelShaders(const int* value) { Lock(); m_hacks.DisablePixelShaders = *value; Unlock(); } - void GetUncapFramerate(int* value) { Lock(); *value = m_hacks.UncapFramerate; Unlock(); } - void SetUncapFramerate(const int* value) { Lock(); m_hacks.UncapFramerate = *value; Unlock(); } void GetUseAllCores(int* value) { Lock(); *value = m_hacks.UseAllCores; Unlock(); } void SetUseAllCores(const int* value) { Lock(); m_hacks.UseAllCores = *value; Unlock(); } void GetSkipRdtscPatching(int* value) { Lock(); *value = m_hacks.SkipRdtscPatching; Unlock(); } diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index c71b55b7d..4d7bfb04e 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -57,6 +57,7 @@ namespace xboxkrnl #include #include #include +#include // Allow use of time duration literals (making 16ms, etc possible) using namespace std::literals::chrono_literals; @@ -168,7 +169,8 @@ static DWORD g_dwVertexShaderUsage = 0; static DWORD g_VertexShaderSlots[136]; DWORD g_XboxBaseVertexIndex = 0; - +DWORD g_DefaultPresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; +DWORD g_PresentationIntervalOverride = 0; // Active D3D Vertex Streams (and strides) XTL::X_D3DVertexBuffer*g_D3DStreams[16]; @@ -1916,21 +1918,14 @@ static DWORD WINAPI EmuCreateDeviceProxy(LPVOID) { g_EmuCDPD.HostPresentationParameters.Windowed = !g_XBVideo.bFullScreen; - if(g_XBVideo.bVSync) - g_EmuCDPD.HostPresentationParameters.SwapEffect = XTL::D3DSWAPEFFECT_COPY; // Was D3DSWAPEFFECT_COPY_VSYNC; + // TODO: Investigate the best option for this + g_EmuCDPD.HostPresentationParameters.SwapEffect = XTL::D3DSWAPEFFECT_COPY; g_EmuCDPD.HostPresentationParameters.BackBufferFormat = XTL::EmuXB2PC_D3DFormat(g_EmuCDPD.XboxPresentationParameters.BackBufferFormat); g_EmuCDPD.HostPresentationParameters.AutoDepthStencilFormat = XTL::EmuXB2PC_D3DFormat(g_EmuCDPD.XboxPresentationParameters.AutoDepthStencilFormat); - if(!g_XBVideo.bVSync && (g_D3DCaps.PresentationIntervals & D3DPRESENT_INTERVAL_IMMEDIATE) && g_XBVideo.bFullScreen) - g_EmuCDPD.HostPresentationParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - else - { - if(g_D3DCaps.PresentationIntervals & D3DPRESENT_INTERVAL_ONE && g_XBVideo.bFullScreen) - g_EmuCDPD.HostPresentationParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_ONE; - else - g_EmuCDPD.HostPresentationParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - } + g_EmuCDPD.HostPresentationParameters.PresentationInterval = g_XBVideo.bVSync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; + g_DefaultPresentationInterval = g_EmuCDPD.XboxPresentationParameters.PresentationInterval; // HACK: Disable Tripple Buffering for now... // TODO: Enumerate maximum BackBufferCount if possible. @@ -4555,19 +4550,50 @@ DWORD WINAPI XTL::EMUPATCH(D3DDevice_Swap) hRet = g_pD3DDevice->BeginScene(); - if (!g_UncapFramerate) { - // If the last frame completed faster than the Xbox VBlank period, wait for it - // TODO: Read the frame rate target from the Xbox display mode - // See comments in GetNextVblankTime(); - auto targetDuration = 16.6666666667ms; - while (std::chrono::high_resolution_clock::now() - frameStartTime < targetDuration) { - // We use an empty while loop because actually sleeping is too unstable - // Sleeping causes the frame duration to jitter... - ; - } + // Check if we need to enable our frame-limiter + DWORD presentationInverval = g_PresentationIntervalOverride > 0 ? g_PresentationIntervalOverride : g_DefaultPresentationInterval; + if (presentationInverval != D3DPRESENT_INTERVAL_IMMEDIATE) { + // If the last frame completed faster than the Xbox target swap rate, wait for it - frameStartTime = std::chrono::high_resolution_clock::now(); - } + auto targetRefreshRate = 60.0f; // TODO: Read from Xbox Display Mode + + // Determine how many 'frames' worth of time we need to wait for + // This allows games that require a locked framerate (eg JSRF) to function correctly + // While allowing titles with an unlocked frame-rate to not be limited + auto multiplier = 1.0f; + switch (presentationInverval) { + case D3DPRESENT_INTERVAL_ONE: + case 0x80000001: // D3DPRESENT_INTERVAL_ONE_OR_IMMEDIATE: + multiplier = 1.0f; + break; + case D3DPRESENT_INTERVAL_TWO: + case 0x80000002: // D3DPRESENT_INTERVAL_TWO_OR_IMMEDIATE: + multiplier = 2.0f; + break; + case D3DPRESENT_INTERVAL_THREE: + multiplier = 3.0f; + break; + case D3DPRESENT_INTERVAL_FOUR: + multiplier = 4.0f; + break; + } + + auto targetDuration = std::chrono::duration(((1000.0f / targetRefreshRate) * multiplier)); + auto targetTimestamp = frameStartTime + targetDuration; + + // If we need to wait for a larger amount of time (>= 1 frame at 60FPS), we can just sleep + if (std::chrono::duration_cast(targetTimestamp - std::chrono::high_resolution_clock::now()).count() > 16) { + std::this_thread::sleep_until(targetTimestamp); + } else { + // Otherwise, we fall-through and just keep polling + // This prevents large waits from hogging CPU power, but allows small waits/ to remain precice. + while (std::chrono::high_resolution_clock::now() < targetTimestamp) { + ; + } + } + } + + frameStartTime = std::chrono::high_resolution_clock::now(); UpdateFPSCounter(); diff --git a/src/core/hle/D3D8/XbState.cpp b/src/core/hle/D3D8/XbState.cpp index b7e31c317..99034010a 100644 --- a/src/core/hle/D3D8/XbState.cpp +++ b/src/core/hle/D3D8/XbState.cpp @@ -79,7 +79,12 @@ void UpdateDeferredRenderStates() // Certain D3DRS values need to be checked on each Draw[Indexed]Vertices if (XTL::EmuD3DDeferredRenderState != 0) { // Loop through all deferred render states - for (unsigned int RenderState = XTL::X_D3DRS_FOGENABLE; RenderState <= XTL::X_D3DRS_PRESENTATIONINTERVAL; RenderState++) { + for (unsigned int RenderState = XTL::X_D3DRS_FOGENABLE; RenderState <= XTL::X_D3DRS_PRESENTATIONINTERVAL; RenderState++) { + // If the current state is not present within our desired XDK, skip it + if (XTL::DxbxRenderStateInfo[RenderState].V >= g_BuildVersion) { + continue; + } + uint8_t index = RenderState - XTL::X_D3DRS_FOGENABLE; DWORD Value = XTL::EmuD3DDeferredRenderState[index]; @@ -117,10 +122,16 @@ void UpdateDeferredRenderStates() case XTL::X_D3DRS_BACKAMBIENTMATERIALSOURCE: case XTL::X_D3DRS_BACKEMISSIVEMATERIALSOURCE: case XTL::X_D3DRS_BACKAMBIENT: - case XTL::X_D3DRS_SWAPFILTER: - case XTL::X_D3DRS_PRESENTATIONINTERVAL: - // These render states are unsupported by the host, so we skip them entirely + case XTL::X_D3DRS_SWAPFILTER: + // These states are unsupported by the host and are ignored (for now) continue; + case XTL::X_D3DRS_PRESENTATIONINTERVAL: { + // Store this as an override for our frame limiter + // Games can use this to limit certain scenes to a desired target framerate for a specific scene + // If this value is not set, or is set to 0, the default interval passed to CreateDevice is used + extern DWORD g_PresentationIntervalOverride; + g_PresentationIntervalOverride = Value; + } continue; case XTL::X_D3DRS_WRAP0: case XTL::X_D3DRS_WRAP1: case XTL::X_D3DRS_WRAP2: @@ -229,7 +240,7 @@ void UpdateDeferredTextureStates() // If point sprites are enabled, we need to overwrite our existing state 0 with State 3 also DWORD HostStage = (pointSpriteOverride && XboxStage == 3) ? 0 : XboxStage; - for (int StateIndex = XTL::X_D3DTSS_DEFERRED_FIRST; StateIndex <= XTL::X_D3DTSS_DEFERRED_LAST; StateIndex++) { + for (int StateIndex = XTL::X_D3DTSS_DEFERRED_FIRST; StateIndex <= XTL::X_D3DTSS_DEFERRED_LAST; StateIndex++) { // Read the value of the current stage/state from the Xbox data structure DWORD Value = XTL::EmuD3DDeferredTextureState[(XboxStage * XTL::X_D3DTS_STAGESIZE) + StateIndex]; diff --git a/src/core/hle/Patches.cpp b/src/core/hle/Patches.cpp index d73b6105e..8002b1fb6 100644 --- a/src/core/hle/Patches.cpp +++ b/src/core/hle/Patches.cpp @@ -375,7 +375,6 @@ std::map g_PatchTable = { PATCH_ENTRY("GetExitCodeThread", XTL::EMUPATCH(GetExitCodeThread), PATCH_ALWAYS), PATCH_ENTRY("GetThreadPriority", XTL::EMUPATCH(GetThreadPriority), PATCH_ALWAYS), PATCH_ENTRY("OutputDebugStringA", XTL::EMUPATCH(OutputDebugStringA), PATCH_ALWAYS), - PATCH_ENTRY("QueryPerformanceCounter", XTL::EMUPATCH(QueryPerformanceCounter), PATCH_ALWAYS), PATCH_ENTRY("RaiseException", XTL::EMUPATCH(RaiseException), PATCH_ALWAYS), PATCH_ENTRY("SetThreadPriority", XTL::EMUPATCH(SetThreadPriority), PATCH_ALWAYS), PATCH_ENTRY("SetThreadPriorityBoost", XTL::EMUPATCH(SetThreadPriorityBoost), PATCH_ALWAYS), diff --git a/src/core/hle/XAPI/Xapi.cpp b/src/core/hle/XAPI/Xapi.cpp index 84d539141..9a2a6e702 100644 --- a/src/core/hle/XAPI/Xapi.cpp +++ b/src/core/hle/XAPI/Xapi.cpp @@ -1484,21 +1484,6 @@ LPVOID WINAPI XTL::EMUPATCH(ConvertThreadToFiber) RETURN(pRet); } -// ****************************************************************** -// * patch: QueryPerformanceCounter -// ****************************************************************** -ULONGLONG CxbxGetPerformanceCounter(bool acpi); // implemented in EmuKrnlKe.cpp -BOOL WINAPI XTL::EMUPATCH(QueryPerformanceCounter) -( - LARGE_INTEGER * lpPerformanceCount -) -{ - // NOTE: QueryPerformanceCounter runs from the tsc via RdTsc (733mhz) - // However, KeQueryPerformanceCounter runs at 3.375Mhz, so we can't use that here - lpPerformanceCount->QuadPart = CxbxGetPerformanceCounter(false); - return TRUE; -} - // ****************************************************************** // * patch: QueueUserAPC // ****************************************************************** diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index a0324ecbc..fa4ce9524 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -608,7 +608,6 @@ void PrintCurrentConfigurationLog() { printf("--------------------------- HACKS CONFIG ---------------------------\n"); printf("Disable Pixel Shaders: %s\n", g_DisablePixelShaders == 1 ? "On" : "Off (Default)"); - printf("Uncap Framerate: %s\n", g_UncapFramerate == 1 ? "On" : "Off (Default)"); printf("Run Xbox threads on all cores: %s\n", g_UseAllCores == 1 ? "On" : "Off (Default)"); printf("Skip RDTSC Patching: %s\n", g_SkipRdtscPatching == 1 ? "On" : "Off (Default)"); printf("Scale Xbox to host viewport (and back): %s\n", g_ScaleViewport == 1 ? "On" : "Off (Default)"); @@ -1407,8 +1406,6 @@ __declspec(noreturn) void CxbxKrnlInit int HackEnabled = 0; g_EmuShared->GetDisablePixelShaders(&HackEnabled); g_DisablePixelShaders = !!HackEnabled; - g_EmuShared->GetUncapFramerate(&HackEnabled); - g_UncapFramerate = !!HackEnabled; g_EmuShared->GetUseAllCores(&HackEnabled); g_UseAllCores = !!HackEnabled; g_EmuShared->GetSkipRdtscPatching(&HackEnabled); diff --git a/src/core/kernel/support/Emu.cpp b/src/core/kernel/support/Emu.cpp index b0fadf749..134dd796b 100644 --- a/src/core/kernel/support/Emu.cpp +++ b/src/core/kernel/support/Emu.cpp @@ -53,7 +53,6 @@ volatile thread_local bool g_bEmuException = false; volatile bool g_bEmuSuspended = false; volatile bool g_bPrintfOn = true; bool g_DisablePixelShaders = false; -bool g_UncapFramerate = false; bool g_UseAllCores = false; bool g_SkipRdtscPatching = false; bool g_ScaleViewport = false; diff --git a/src/core/kernel/support/Emu.h b/src/core/kernel/support/Emu.h index 8da6ec3cb..74f7300b5 100644 --- a/src/core/kernel/support/Emu.h +++ b/src/core/kernel/support/Emu.h @@ -102,7 +102,6 @@ typedef struct DUMMY_KERNEL extern bool g_DisablePixelShaders; -extern bool g_UncapFramerate; extern bool g_UseAllCores; extern bool g_SkipRdtscPatching; extern bool g_ScaleViewport; diff --git a/src/gui/ResCxbx.h b/src/gui/ResCxbx.h index 3551f1947..7beaf5a7a 100644 --- a/src/gui/ResCxbx.h +++ b/src/gui/ResCxbx.h @@ -342,7 +342,6 @@ #define ID_SETTINGS_INITIALIZE 40091 #define ID_EMULATION_STARTDEBUGGER 40092 #define ID_FPS 40096 -#define ID_HACKS_UNCAPFRAMERATE 40097 #define ID_HACKS_RUNXBOXTHREADSONALLCORES 40098 #define ID_HACKS_SKIPRDTSCPATCHING 40099 #define ID_HACKS_SCALEVIEWPORT 40100 diff --git a/src/gui/WndMain.cpp b/src/gui/WndMain.cpp index 4d201e418..452ef3f9d 100644 --- a/src/gui/WndMain.cpp +++ b/src/gui/WndMain.cpp @@ -1248,11 +1248,6 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP RefreshMenus(); break; - case ID_HACKS_UNCAPFRAMERATE: - g_Settings->m_hacks.UncapFramerate = !g_Settings->m_hacks.UncapFramerate; - RefreshMenus(); - break; - case ID_HACKS_RUNXBOXTHREADSONALLCORES: g_Settings->m_hacks.UseAllCores = !g_Settings->m_hacks.UseAllCores; RefreshMenus(); @@ -1712,9 +1707,6 @@ void WndMain::RefreshMenus() chk_flag = (g_Settings->m_hacks.DisablePixelShaders) ? MF_CHECKED : MF_UNCHECKED; CheckMenuItem(settings_menu, ID_HACKS_DISABLEPIXELSHADERS, chk_flag); - chk_flag = (g_Settings->m_hacks.UncapFramerate) ? MF_CHECKED : MF_UNCHECKED; - CheckMenuItem(settings_menu, ID_HACKS_UNCAPFRAMERATE, chk_flag); - chk_flag = (g_Settings->m_hacks.UseAllCores) ? MF_CHECKED : MF_UNCHECKED; CheckMenuItem(settings_menu, ID_HACKS_RUNXBOXTHREADSONALLCORES, chk_flag);