Merge pull request #1581 from LukeUsher/rdtsc-vsync-and-timing

Respect Xbox presentation interval
This commit is contained in:
PatrickvL 2019-04-09 09:20:46 +02:00 committed by GitHub
commit 2e5b9562e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 72 additions and 68 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

@ -205,9 +205,12 @@ public:
} m_controller_port;
// 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 Reserved2;
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

@ -57,6 +57,7 @@ namespace xboxkrnl
#include <process.h>
#include <clocale>
#include <unordered_map>
#include <thread>
// 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<double, std::milli>(((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<std::chrono::milliseconds>(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();

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