mirror of
https://github.com/InstanceBrick/nightcap.git
synced 2026-10-17 19:00:48 +02:00
The main thread polls nativeCallMessagesFromMainThread 146k times a second with nothing to do. Between polls it now rests 100 us: nanosleep when the governor is performance, TPAUSE (C0.2) when the CPU has WAITPKG, a spin otherwise. Sleeping under a power-saving governor lets the render thread's core clock down and drops frames, which is why TPAUSE is the default there. MOCKTAIL_ENGINE_PUMP_REST=sleep|tpause|spin overrides. The profile trace gains a slice for pumps that ran a message.
66 lines
2.7 KiB
C++
66 lines
2.7 KiB
C++
#ifndef MOCKTAIL_RUNTIME_ENGINE_PUMP_REST_H_
|
|
#define MOCKTAIL_RUNTIME_ENGINE_PUMP_REST_H_
|
|
|
|
#include <cstdint>
|
|
|
|
namespace mocktail::runtime {
|
|
|
|
// The engine posts its per-frame main-thread step at a time of its own
|
|
// choosing, with no signal the host can block on, so the host main thread
|
|
// polls nativeCallMessagesFromMainThread. An empty poll costs a few
|
|
// microseconds; polled back to back it burns a core.
|
|
//
|
|
// Between polls the thread rests for kEnginePumpRestNs. A post is then picked
|
|
// up at most kEnginePumpRestNs + timer slack + wake latency late, far under
|
|
// the engine's sub-millisecond slack.
|
|
//
|
|
// How it rests decides the clock. Under a power-saving governor the CPU clocks
|
|
// a bursty core down unless some core stays awake, and the engine's render
|
|
// thread then runs slower and drops frames. So:
|
|
// kSleep nanosleep. The core idles. Correct only when the governor holds
|
|
// the clock up on its own (performance, which GameMode sets).
|
|
// kTpause TPAUSE in C0.2. The core stays awake at reduced power, the clock
|
|
// holds, and the OS books the time as busy. Needs WAITPKG.
|
|
// kSpin No rest. The core stays awake at full power.
|
|
inline constexpr uint64_t kEnginePumpRestNs = 100'000;
|
|
// Timer slack the calling thread is set to; the kernel default of 50 us would
|
|
// otherwise stretch every sleep.
|
|
inline constexpr uint64_t kEnginePumpTimerSlackNs = 10'000;
|
|
// Shallow C-state exit plus scheduler latency on the host, for the bound.
|
|
inline constexpr uint64_t kShallowWakeNs = 50'000;
|
|
|
|
enum class EnginePumpRestMode { kSleep, kTpause, kSpin };
|
|
|
|
const char* EnginePumpRestModeName(EnginePumpRestMode mode);
|
|
|
|
struct EnginePumpRestInputs {
|
|
bool governor_performance = false;
|
|
bool cpu_has_waitpkg = false;
|
|
// MOCKTAIL_ENGINE_PUMP_REST: "sleep", "tpause" or "spin". Null, empty or
|
|
// unknown defers to the policy; "tpause" without WAITPKG does too.
|
|
const char* override = nullptr;
|
|
};
|
|
|
|
EnginePumpRestMode ChooseEnginePumpRestMode(const EnginePumpRestInputs& inputs);
|
|
|
|
// True when the file holds "performance". `path` is a cpufreq
|
|
// scaling_governor file; cpu0's stands for the machine.
|
|
bool CpuGovernorIsPerformance(const char* path);
|
|
|
|
bool CpuHasWaitPkg();
|
|
|
|
// The mode chosen from the host on first use.
|
|
EnginePumpRestMode ActiveEnginePumpRestMode();
|
|
|
|
// Rests the calling thread in the given mode and returns the rest length, 0
|
|
// for kSpin. The first call lowers the thread's timer slack to
|
|
// kEnginePumpTimerSlackNs.
|
|
uint64_t RestAfterEnginePump(EnginePumpRestMode mode);
|
|
inline uint64_t RestAfterEnginePump() {
|
|
return RestAfterEnginePump(ActiveEnginePumpRestMode());
|
|
}
|
|
|
|
} // namespace mocktail::runtime
|
|
|
|
#endif // MOCKTAIL_RUNTIME_ENGINE_PUMP_REST_H_
|