xenia-project / xenia

Xbox 360 Emulator Research Project
https://xenia.jp
Other
8.15k stars 1.12k forks source link

Power-saving settings harm Xenia's performance #801

Closed wolfokami321 closed 5 years ago

wolfokami321 commented 6 years ago

I noticed an odd error, unless I open google chrome, the framerate for xenia is 30fps or lower. This is repeatable. The graphics card I am using is the Nvidia 965m. Specifically youtube or discord speeds up the game for some reason. I'm also using Allancat's xenia build for Sonic 2006

maxton commented 6 years ago

I've heard issues like this before, the solution was disabling power-saving mode on your CPU (on windows, set your power profile to High Performance)

JoaRiski commented 6 years ago

Try killing dwm.exe and see if that has any effect.

Margen67 commented 6 years ago

@JoaRiski DWM resets itself on Windows 8+

JoaRiski commented 6 years ago

Another option could be to disable Windows Aero, though I'm not sure if it's relevant to this case. I've experienced similar issues with framerate and killing dwm and/or disabling Aero has helped.

wolfokami321 commented 6 years ago

ive tried both with no result

Blayer98 commented 6 years ago

I've had this issue as well, however, I use a NVidia GeForce GT 650M, and Scrolling in Google Chrome or having OBS open will either make the framerate or the audio speed back up to 60fps, or both. I made a thread on reddit about it, and none of the suggestions here worked for me.

0x8080 commented 6 years ago

@Blayer98 Check your power management setting in both the Windows and Nvidia control panel, also make sure Xenia is set to use the dedicated Nvidia GPU and NOT the Intel iGPU.

Blayer98 commented 6 years ago

Changing Windows power management to Performance didn't work, and even when using the NVidia GPU, it doesn't improve the framerate or audio. Setting NVidia to Performance didn't work either. @shadowbrony33

Blayer98 commented 6 years ago

I've had both on as well and that does not work either, there has to be an issue with Xenia...

Nukem9 commented 6 years ago

timeBeginPeriod(0); might fix it. It sets a global windows timer and affects all processes. By default it's set to 10 and chrome may be setting it to 0 for better performance.

Blayer98 commented 6 years ago

Hmm, I could test that but I don't have time unfortunately, I will see if I can build Xenia at home later but no promises...

Blayer98 commented 6 years ago

I actually got a reddit reply from Dark-Star_1337:

"Both Chrome and OBS switch the timer tick interval in Windows to 1ms (the default is around 15ms I think), probably that's what causes Xenia to speed up. Just google "Chrome timer tick rate" or something like that."

Margen67 commented 6 years ago

Try this: http://www.lucashale.com/timerresolution/TimerResolution.zip

Press 'Maximum' and see if it does anything.

ghost commented 6 years ago

Wouldn't the timeBeginPeriod(0); be a good addition to xenia since it is pretty CPU heavy?

Blayer98 commented 6 years ago

@Margen67 That tool you linked works great! Bejeweled Blitz LIVE still runs slow, but at least the audio is playing at normal speed. I pressed Maximum and the Resolution was set to 0.500 milliseconds. The default for me was 15.625.

Blayer98 commented 6 years ago

Game Room while using the tool plays audio at normal speed as well and the game runs faster.

DrChat commented 6 years ago

@Margen67 Is that tool open source?

Margen67 commented 6 years ago

No. https://cms.lucashale.com/timer-resolution/

hlide commented 6 years ago

That tool is not necessary, you only need to set the timer resolution to 0.5 ms through a kernel32 function. I used it for my PSP emulator to get a PSP thread to process audio at 1 ms resolution when it uses very small packets to play audio samples.

hlide commented 6 years ago
ULONG          hal::os::Clock::m_old_resolution;
ULONG          hal::os::Clock::m_max_resolution;
f64            hal::os::Clock::m_qpc_resolution;
f64            hal::os::Clock::m_qpc_factor;
f64            hal::os::Clock::m_qpc_frequency;
s64            hal::os::Clock::m_tsc_frequency;
hal::os::Clock hal::os::Clock::m_instance(0);

hal::os::Clock::Clock()
{
}

hal::os::Clock::Clock(int)
{
    ULONG     dummy;
    s64       pca, pcb, pcf, tsca, tscb;
    f64       interval;
    int const times = 16;
    int const retries = 5;

    // install the fastest timer resolution (should be 0.5 ms)
    ::NtQueryTimerResolution(&dummy, &m_max_resolution, &m_old_resolution);
    ::NtSetTimerResolution(m_max_resolution, TRUE, &dummy);
    ::NtQueryPerformanceCounter((PLARGE_INTEGER)&pca, (PLARGE_INTEGER)&pcf);

    m_qpc_frequency  = f64(pcf);
    m_qpc_factor     = 1000000.0 / m_qpc_frequency;
    m_qpc_resolution = f64(m_max_resolution) / 10.0;

    m_tsc_frequency = 0;
    f64 frequency = 0.0;

    // try to compute the CPU frequency with the highest thread priority
    __ThreadPriority(THREAD_PRIORITY_TIME_CRITICAL)
    {
        for (int j = 0; j < times; ++j)
        {
            for (int i = 0; i < retries; ++i)
            {
                ReadCounters(tsca, pca);

                do
                {
                    ReadCounters(tscb, pcb);
                    interval = f64(pcb - pca) * m_qpc_factor;
                }
                while (interval < 1000.0);

                if (interval < 1001.0)
                {
                    frequency += f64(tscb - tsca) / interval;
                    break;
                }
            }
        }
    }

    m_tsc_frequency = s64(frequency / f64(times));
}

hal::os::Clock::~Clock()
{
    if (this == &m_instance)
    {
        ULONG dummy;

        // restore the original timer resolution
        ::NtSetTimerResolution(m_old_resolution, FALSE, &dummy);
    }
}

...
        struct ScopedThreadPriority
        {
            /**/        ScopedThreadPriority(int new_priority = THREAD_PRIORITY_TIME_CRITICAL) : old_priority(::GetThreadPriority((HANDLE)0xFFFFFFFEull))
            {
                ::SetThreadPriority((HANDLE)0xFFFFFFFEull, new_priority);
            }

            /**/       ~ScopedThreadPriority()
            {
                ::SetThreadPriority((HANDLE)0xFFFFFFFEull, old_priority);
            }

            operator bool() const { return true; }

        private:
            int old_priority;
        };

#define __ThreadPriority(priority) if (::hal::os::ScopedThreadPriority const __stp__ = ::hal::os::ScopedThreadPriority(priority))
DrChat commented 6 years ago

Thanks @hlide - I'll look into this.

slx7R4GDZM commented 6 years ago

Not sure if you already got it but this tool is open source. https://github.com/tebjan/TimerTool

DrChat commented 6 years ago

Question: Do AAA games use this same hack? This is an internal API call - one thing I'm worried about is that it'll change across versions of Windows.

CornflakeRush commented 6 years ago

I had an issue with Sonic The Hedgehog 2006 running well the first time I tried it, then every time I tried to play the game in Xenia after that the sounds would play at the correct speed but everything else would be in slow motion, even though I was already on the Max Performance power setting.

Using the Timer Tool listed by @slx7R4GDZM seems to have fixed this issue for me!

Jeremy517 commented 6 years ago

@DrChat, Chrome definitely calls it. I think it is pretty safe to assume it will be around for the long-term; it has been around since at least Windows 2000.

If you are still worried, though, we can use GetProcAddress and then just not do anything if the proc isn't found:

typedef LONG(CALLBACK* NTSETTIMERRESOLUTION)(IN ULONG DesiredTime,
                                                 IN BOOLEAN SetResolution,
                                                 OUT PULONG ActualTime);
NTSETTIMERRESOLUTION NtSetTimerResolution;

typedef LONG(CALLBACK* NTQUERYTIMERRESOLUTION)(OUT PULONG MaximumTime,
                                                   OUT PULONG MinimumTime,
                                                   OUT PULONG CurrentTime);
NTQUERYTIMERRESOLUTION NtQueryTimerResolution;

  // On startup
  ULONG dummy;
  ULONG m_old_resolution = -1;
  ULONG m_max_resolution;

  HMODULE hNtDll = LoadLibrary(L"NtDll.dll");
  if (hNtDll) {
    NtQueryTimerResolution = (NTQUERYTIMERRESOLUTION)GetProcAddress(hNtDll, "NtQueryTimerResolution");
    NtSetTimerResolution = (NTSETTIMERRESOLUTION)GetProcAddress(hNtDll, "NtSetTimerResolution");
    if (NtQueryTimerResolution && NtSetTimerResolution) {
      NtQueryTimerResolution(&dummy, &m_max_resolution, &m_old_resolution);
      NtSetTimerResolution(m_max_resolution, TRUE, &dummy);
    }
  }

  // On exit
  if (hNtDll) {
    if (m_old_resolution != -1) {
      NtSetTimerResolution(m_old_resolution, FALSE, &dummy);
    }
    FreeLibrary(hNtDll);
  }

Or the alternative (without GetProcAddress approach) that hlide gave:

extern "C" NTSYSAPI LONG NTAPI NtSetTimerResolution(
    ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution);
extern "C" NTSYSAPI LONG NTAPI NtQueryTimerResolution(
    OUT PULONG MinimumResolution, OUT PULONG MaximumResolution,
    OUT PULONG CurrentResolution);

  // On startup
  ::NtQueryTimerResolution(&dummy, &m_max_resolution, &m_old_resolution);
  ::NtSetTimerResolution(m_max_resolution, TRUE, &dummy);

  // On exit
  ::NtSetTimerResolution(m_old_resolution, FALSE, &dummy); 

Both approaches seem to work perfectly. I was wondering why I usually got half framerate on Banjo-Kazooie, and this was the anwer.

slx7R4GDZM commented 6 years ago

It looks like RPCS3 is having a similar issue. https://github.com/RPCS3/rpcs3/pull/4579

DrChat commented 6 years ago

Should be fixed as of 6262dd2c3d30cbc13e653b42d6691c5388433ca6.

xperia64 commented 6 years ago

I experienced what would seem to be this issue on a recent build, but I remembered I was playing with my HPET settings recently to address an unrelated problem.

For anyone else experiencing what seems like this issue: Xenia was running at around half speed when I had the following set in my BCD config:

tscsyncpolicy           Enhanced
useplatformclock        Yes
disabledynamictick      Yes

Setting the values to the following solved the issue:

tscsyncpolicy           Default
useplatformclock        No
disabledynamictick      No

I'm not sure which one in particular fixed it, but at least one of them was making Xenia lag significantly.

Using the timer tool linked above, the min and max times had not changed at all between adjusting these HPET-related settings.

My "QueryPerformanceFrequency" however had dropped from 24.00000 MHz before making the change to 4.10157 MHz afterwards.

mirh commented 6 years ago

HPET has a lot of caveats. But I'm not sure what it would have to do with chrome.

Margen67 commented 5 years ago

Power-saving settings affects performance in all applications, not just xenia.

DBZGohan commented 5 years ago

How do I make my game run faster... I'm trying to make Dragon Ball Z: Ultimate Tenkaichi run faster but it goes so slow... My power saving setting is turned to best performance its still slow???

Triang3l commented 5 years ago

@DBZGohan Only by hardcore programming, or maybe we can eventually, but there are no simple switches.