Respect Xbox presentation interval

This allows us to improve our frame-limiter, so that we can disable it
if a game does not require a fixed frame-rate, and enable it for titles
that do.

Result:
XDK Samples can now run at high frame-rates (100+ fps) without effecting actual
speed (just like on real hardware)

Dashboard can reach over 100fps, and still run at the correct speed

JSRF and other titles that tie game speed to frame-rate are limited to
60FPS.

Fixed broken VSYNC handling too: Disable VSYNC has been broken since the
D3D9 port, perhaps before then.
This commit is contained in:
Luke Usher 2019-04-04 16:10:45 +01:00
parent 13daf887c0
commit 8b2e758377
13 changed files with 55 additions and 67 deletions

View File

@ -617,7 +617,6 @@ BEGIN
BEGIN BEGIN
MENUITEM "Run Xbox threads on all cores", ID_HACKS_RUNXBOXTHREADSONALLCORES,MFT_STRING,MFS_ENABLED 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 "Render directly to Host Backbuffer", ID_HACKS_RENDERDIRECTLYTOHOSTBACKBUFFER,MFT_STRING,MFS_ENABLED
MENUITEM "Uncap Framerate", ID_HACKS_UNCAPFRAMERATE,MFT_STRING,MFS_ENABLED
END END
MENUITEM "Disable Pixel Shaders", ID_HACKS_DISABLEPIXELSHADERS,MFT_STRING,MFS_ENABLED MENUITEM "Disable Pixel Shaders", ID_HACKS_DISABLEPIXELSHADERS,MFT_STRING,MFS_ENABLED
MENUITEM "Skip rdtsc patching", ID_HACKS_SKIPRDTSCPATCHING,MFT_STRING,MFS_ENABLED MENUITEM "Skip rdtsc patching", ID_HACKS_SKIPRDTSCPATCHING,MFT_STRING,MFS_ENABLED

View File

@ -118,7 +118,6 @@ static struct {
static const char* section_hack = "hack"; static const char* section_hack = "hack";
static struct { static struct {
const char* DisablePixelShaders = "DisablePixelShaders"; const char* DisablePixelShaders = "DisablePixelShaders";
const char* UncapFramerate = "UncapFramerate";
const char* UseAllCores = "UseAllCores"; const char* UseAllCores = "UseAllCores";
const char* SkipRdtscPatching = "SkipRdtscPatching"; const char* SkipRdtscPatching = "SkipRdtscPatching";
const char* ScaleViewPort = "ScaleViewPort"; const char* ScaleViewPort = "ScaleViewPort";
@ -376,7 +375,6 @@ bool Settings::LoadConfig()
// ==== Hack Begin ========== // ==== Hack Begin ==========
m_hacks.DisablePixelShaders = m_si.GetBoolValue(section_hack, sect_hack_keys.DisablePixelShaders, /*Default=*/false); 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.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.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); 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 ========== // ==== Hack Begin ==========
m_si.SetBoolValue(section_hack, sect_hack_keys.DisablePixelShaders, m_hacks.DisablePixelShaders, nullptr, true); 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.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.SkipRdtscPatching, m_hacks.SkipRdtscPatching, nullptr, true);
m_si.SetBoolValue(section_hack, sect_hack_keys.ScaleViewPort, m_hacks.ScaleViewport, nullptr, true); m_si.SetBoolValue(section_hack, sect_hack_keys.ScaleViewPort, m_hacks.ScaleViewport, nullptr, true);

View File

@ -207,7 +207,6 @@ public:
// Hack settings // Hack settings
struct s_hack { struct s_hack {
bool DisablePixelShaders; bool DisablePixelShaders;
bool UncapFramerate;
bool UseAllCores; bool UseAllCores;
bool SkipRdtscPatching; bool SkipRdtscPatching;
bool ScaleViewport; bool ScaleViewport;

View File

@ -143,8 +143,6 @@ class EmuShared : public Mutex
void GetDisablePixelShaders(int* value) { Lock(); *value = m_hacks.DisablePixelShaders; Unlock(); } void GetDisablePixelShaders(int* value) { Lock(); *value = m_hacks.DisablePixelShaders; Unlock(); }
void SetDisablePixelShaders(const int* value) { Lock(); m_hacks.DisablePixelShaders = *value; 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 GetUseAllCores(int* value) { Lock(); *value = m_hacks.UseAllCores; Unlock(); }
void SetUseAllCores(const int* value) { Lock(); m_hacks.UseAllCores = *value; Unlock(); } void SetUseAllCores(const int* value) { Lock(); m_hacks.UseAllCores = *value; Unlock(); }
void GetSkipRdtscPatching(int* value) { Lock(); *value = m_hacks.SkipRdtscPatching; Unlock(); } void GetSkipRdtscPatching(int* value) { Lock(); *value = m_hacks.SkipRdtscPatching; Unlock(); }

View File

@ -168,7 +168,8 @@ static DWORD g_dwVertexShaderUsage = 0;
static DWORD g_VertexShaderSlots[136]; static DWORD g_VertexShaderSlots[136];
DWORD g_XboxBaseVertexIndex = 0; DWORD g_XboxBaseVertexIndex = 0;
DWORD g_DefaultPresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
DWORD g_PresentationIntervalOverride = 0;
// Active D3D Vertex Streams (and strides) // Active D3D Vertex Streams (and strides)
XTL::X_D3DVertexBuffer*g_D3DStreams[16]; XTL::X_D3DVertexBuffer*g_D3DStreams[16];
@ -1916,21 +1917,11 @@ static DWORD WINAPI EmuCreateDeviceProxy(LPVOID)
{ {
g_EmuCDPD.HostPresentationParameters.Windowed = !g_XBVideo.bFullScreen; g_EmuCDPD.HostPresentationParameters.Windowed = !g_XBVideo.bFullScreen;
if(g_XBVideo.bVSync)
g_EmuCDPD.HostPresentationParameters.SwapEffect = XTL::D3DSWAPEFFECT_COPY; // Was D3DSWAPEFFECT_COPY_VSYNC;
g_EmuCDPD.HostPresentationParameters.BackBufferFormat = XTL::EmuXB2PC_D3DFormat(g_EmuCDPD.XboxPresentationParameters.BackBufferFormat); g_EmuCDPD.HostPresentationParameters.BackBufferFormat = XTL::EmuXB2PC_D3DFormat(g_EmuCDPD.XboxPresentationParameters.BackBufferFormat);
g_EmuCDPD.HostPresentationParameters.AutoDepthStencilFormat = XTL::EmuXB2PC_D3DFormat(g_EmuCDPD.XboxPresentationParameters.AutoDepthStencilFormat); 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.PresentationInterval = g_XBVideo.bVSync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
g_EmuCDPD.HostPresentationParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; g_DefaultPresentationInterval = g_EmuCDPD.XboxPresentationParameters.PresentationInterval;
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;
}
// HACK: Disable Tripple Buffering for now... // HACK: Disable Tripple Buffering for now...
// TODO: Enumerate maximum BackBufferCount if possible. // TODO: Enumerate maximum BackBufferCount if possible.
@ -4555,19 +4546,42 @@ DWORD WINAPI XTL::EMUPATCH(D3DDevice_Swap)
hRet = g_pD3DDevice->BeginScene(); hRet = g_pD3DDevice->BeginScene();
if (!g_UncapFramerate) { // Check if we need to enable our frame-limiter
// If the last frame completed faster than the Xbox VBlank period, wait for it DWORD presentationInverval = g_PresentationIntervalOverride > 0 ? g_PresentationIntervalOverride : g_DefaultPresentationInterval;
// TODO: Read the frame rate target from the Xbox display mode if (presentationInverval != D3DPRESENT_INTERVAL_IMMEDIATE) {
// See comments in GetNextVblankTime(); // If the last frame completed faster than the Xbox target swap rate, wait for it
auto targetDuration = 16.6666666667ms;
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<double, std::milli>(((1.0f / targetRefreshRate) * multiplier) * 1000.0f);
while (std::chrono::high_resolution_clock::now() - frameStartTime < targetDuration) { while (std::chrono::high_resolution_clock::now() - frameStartTime < targetDuration) {
// We use an empty while loop because actually sleeping is too unstable // We use an empty while loop because actually sleeping is too unstable
// Sleeping causes the frame duration to jitter... // Sleeping causes the frame duration to jitter...
; ;
} }
frameStartTime = std::chrono::high_resolution_clock::now();
} }
frameStartTime = std::chrono::high_resolution_clock::now();
UpdateFPSCounter(); UpdateFPSCounter();

View File

@ -80,6 +80,11 @@ void UpdateDeferredRenderStates()
if (XTL::EmuD3DDeferredRenderState != 0) { if (XTL::EmuD3DDeferredRenderState != 0) {
// Loop through all deferred render states // 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; uint8_t index = RenderState - XTL::X_D3DRS_FOGENABLE;
DWORD Value = XTL::EmuD3DDeferredRenderState[index]; DWORD Value = XTL::EmuD3DDeferredRenderState[index];
@ -118,9 +123,15 @@ void UpdateDeferredRenderStates()
case XTL::X_D3DRS_BACKEMISSIVEMATERIALSOURCE: case XTL::X_D3DRS_BACKEMISSIVEMATERIALSOURCE:
case XTL::X_D3DRS_BACKAMBIENT: case XTL::X_D3DRS_BACKAMBIENT:
case XTL::X_D3DRS_SWAPFILTER: case XTL::X_D3DRS_SWAPFILTER:
case XTL::X_D3DRS_PRESENTATIONINTERVAL: // These states are unsupported by the host and are ignored (for now)
// These render states are unsupported by the host, so we skip them entirely
continue; 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_WRAP0:
case XTL::X_D3DRS_WRAP1: case XTL::X_D3DRS_WRAP1:
case XTL::X_D3DRS_WRAP2: case XTL::X_D3DRS_WRAP2:

View File

@ -375,7 +375,6 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("GetExitCodeThread", XTL::EMUPATCH(GetExitCodeThread), PATCH_ALWAYS), PATCH_ENTRY("GetExitCodeThread", XTL::EMUPATCH(GetExitCodeThread), PATCH_ALWAYS),
PATCH_ENTRY("GetThreadPriority", XTL::EMUPATCH(GetThreadPriority), PATCH_ALWAYS), PATCH_ENTRY("GetThreadPriority", XTL::EMUPATCH(GetThreadPriority), PATCH_ALWAYS),
PATCH_ENTRY("OutputDebugStringA", XTL::EMUPATCH(OutputDebugStringA), 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("RaiseException", XTL::EMUPATCH(RaiseException), PATCH_ALWAYS),
PATCH_ENTRY("SetThreadPriority", XTL::EMUPATCH(SetThreadPriority), PATCH_ALWAYS), PATCH_ENTRY("SetThreadPriority", XTL::EMUPATCH(SetThreadPriority), PATCH_ALWAYS),
PATCH_ENTRY("SetThreadPriorityBoost", XTL::EMUPATCH(SetThreadPriorityBoost), PATCH_ALWAYS), PATCH_ENTRY("SetThreadPriorityBoost", XTL::EMUPATCH(SetThreadPriorityBoost), PATCH_ALWAYS),

View File

@ -1484,21 +1484,6 @@ LPVOID WINAPI XTL::EMUPATCH(ConvertThreadToFiber)
RETURN(pRet); 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 // * patch: QueueUserAPC
// ****************************************************************** // ******************************************************************

View File

@ -608,7 +608,6 @@ void PrintCurrentConfigurationLog()
{ {
printf("--------------------------- HACKS CONFIG ---------------------------\n"); printf("--------------------------- HACKS CONFIG ---------------------------\n");
printf("Disable Pixel Shaders: %s\n", g_DisablePixelShaders == 1 ? "On" : "Off (Default)"); 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("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("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)"); 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; int HackEnabled = 0;
g_EmuShared->GetDisablePixelShaders(&HackEnabled); g_EmuShared->GetDisablePixelShaders(&HackEnabled);
g_DisablePixelShaders = !!HackEnabled; g_DisablePixelShaders = !!HackEnabled;
g_EmuShared->GetUncapFramerate(&HackEnabled);
g_UncapFramerate = !!HackEnabled;
g_EmuShared->GetUseAllCores(&HackEnabled); g_EmuShared->GetUseAllCores(&HackEnabled);
g_UseAllCores = !!HackEnabled; g_UseAllCores = !!HackEnabled;
g_EmuShared->GetSkipRdtscPatching(&HackEnabled); g_EmuShared->GetSkipRdtscPatching(&HackEnabled);

View File

@ -53,7 +53,6 @@ volatile thread_local bool g_bEmuException = false;
volatile bool g_bEmuSuspended = false; volatile bool g_bEmuSuspended = false;
volatile bool g_bPrintfOn = true; volatile bool g_bPrintfOn = true;
bool g_DisablePixelShaders = false; bool g_DisablePixelShaders = false;
bool g_UncapFramerate = false;
bool g_UseAllCores = false; bool g_UseAllCores = false;
bool g_SkipRdtscPatching = false; bool g_SkipRdtscPatching = false;
bool g_ScaleViewport = false; bool g_ScaleViewport = false;

View File

@ -102,7 +102,6 @@ typedef struct DUMMY_KERNEL
extern bool g_DisablePixelShaders; extern bool g_DisablePixelShaders;
extern bool g_UncapFramerate;
extern bool g_UseAllCores; extern bool g_UseAllCores;
extern bool g_SkipRdtscPatching; extern bool g_SkipRdtscPatching;
extern bool g_ScaleViewport; extern bool g_ScaleViewport;

View File

@ -342,7 +342,6 @@
#define ID_SETTINGS_INITIALIZE 40091 #define ID_SETTINGS_INITIALIZE 40091
#define ID_EMULATION_STARTDEBUGGER 40092 #define ID_EMULATION_STARTDEBUGGER 40092
#define ID_FPS 40096 #define ID_FPS 40096
#define ID_HACKS_UNCAPFRAMERATE 40097
#define ID_HACKS_RUNXBOXTHREADSONALLCORES 40098 #define ID_HACKS_RUNXBOXTHREADSONALLCORES 40098
#define ID_HACKS_SKIPRDTSCPATCHING 40099 #define ID_HACKS_SKIPRDTSCPATCHING 40099
#define ID_HACKS_SCALEVIEWPORT 40100 #define ID_HACKS_SCALEVIEWPORT 40100

View File

@ -1248,11 +1248,6 @@ LRESULT CALLBACK WndMain::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lP
RefreshMenus(); RefreshMenus();
break; break;
case ID_HACKS_UNCAPFRAMERATE:
g_Settings->m_hacks.UncapFramerate = !g_Settings->m_hacks.UncapFramerate;
RefreshMenus();
break;
case ID_HACKS_RUNXBOXTHREADSONALLCORES: case ID_HACKS_RUNXBOXTHREADSONALLCORES:
g_Settings->m_hacks.UseAllCores = !g_Settings->m_hacks.UseAllCores; g_Settings->m_hacks.UseAllCores = !g_Settings->m_hacks.UseAllCores;
RefreshMenus(); RefreshMenus();
@ -1712,9 +1707,6 @@ void WndMain::RefreshMenus()
chk_flag = (g_Settings->m_hacks.DisablePixelShaders) ? MF_CHECKED : MF_UNCHECKED; chk_flag = (g_Settings->m_hacks.DisablePixelShaders) ? MF_CHECKED : MF_UNCHECKED;
CheckMenuItem(settings_menu, ID_HACKS_DISABLEPIXELSHADERS, chk_flag); 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; chk_flag = (g_Settings->m_hacks.UseAllCores) ? MF_CHECKED : MF_UNCHECKED;
CheckMenuItem(settings_menu, ID_HACKS_RUNXBOXTHREADSONALLCORES, chk_flag); CheckMenuItem(settings_menu, ID_HACKS_RUNXBOXTHREADSONALLCORES, chk_flag);