Merge pull request #1581 from LukeUsher/rdtsc-vsync-and-timing
Respect Xbox presentation interval
This commit is contained in:
commit
2e5b9562e0
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
// ******************************************************************
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue