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
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

View File

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

View File

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

View File

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

View File

@ -168,7 +168,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 +1917,11 @@ 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;
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 +4546,42 @@ 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;
// 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
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) {
// We use an empty while loop because actually sleeping is too unstable
// Sleeping causes the frame duration to jitter...
;
}
frameStartTime = std::chrono::high_resolution_clock::now();
}
frameStartTime = std::chrono::high_resolution_clock::now();
UpdateFPSCounter();

View File

@ -80,6 +80,11 @@ void UpdateDeferredRenderStates()
if (XTL::EmuD3DDeferredRenderState != 0) {
// Loop through all deferred render states
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];
@ -118,9 +123,15 @@ void UpdateDeferredRenderStates()
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
// 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:

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("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),

View File

@ -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
// ******************************************************************

View File

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

View File

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

View File

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

View File

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

View File

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