[GPU] Change FPS limit strategy
This commit is contained in:
parent
b9061e6292
commit
34ed823bfc
|
@ -20,7 +20,10 @@ DEFINE_path(
|
||||||
|
|
||||||
DEFINE_bool(vsync, true, "Enable VSYNC.", "GPU");
|
DEFINE_bool(vsync, true, "Enable VSYNC.", "GPU");
|
||||||
|
|
||||||
DEFINE_uint64(vsync_fps, 60, "VSYNC frames per second", "GPU");
|
DEFINE_uint64(framerate_limit, 60,
|
||||||
|
"Maximum frames per second. 0 = Unlimited frames.\n"
|
||||||
|
"Defaults to 60, when set to 0, and VSYNC is enabled.",
|
||||||
|
"GPU");
|
||||||
|
|
||||||
DEFINE_bool(
|
DEFINE_bool(
|
||||||
gpu_allow_invalid_fetch_constants, true,
|
gpu_allow_invalid_fetch_constants, true,
|
||||||
|
|
|
@ -18,7 +18,7 @@ DECLARE_path(dump_shaders);
|
||||||
|
|
||||||
DECLARE_bool(vsync);
|
DECLARE_bool(vsync);
|
||||||
|
|
||||||
DECLARE_uint64(vsync_fps);
|
DECLARE_uint64(framerate_limit);
|
||||||
|
|
||||||
DECLARE_bool(gpu_allow_invalid_fetch_constants);
|
DECLARE_bool(gpu_allow_invalid_fetch_constants);
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ __declspec(dllexport) uint32_t AmdPowerXpressRequestHighPerformance = 1;
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif // XE_PLATFORM_WIN32
|
#endif // XE_PLATFORM_WIN32
|
||||||
|
|
||||||
GraphicsSystem::GraphicsSystem() : vsync_worker_running_(false) {
|
GraphicsSystem::GraphicsSystem() : frame_limiter_worker_running_(false) {
|
||||||
register_file_ = reinterpret_cast<RegisterFile*>(memory::AllocFixed(
|
register_file_ = reinterpret_cast<RegisterFile*>(memory::AllocFixed(
|
||||||
nullptr, sizeof(RegisterFile), memory::AllocationType::kReserveCommit,
|
nullptr, sizeof(RegisterFile), memory::AllocationType::kReserveCommit,
|
||||||
memory::PageAccess::kReadWrite));
|
memory::PageAccess::kReadWrite));
|
||||||
|
@ -100,55 +100,79 @@ X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||||
reinterpret_cast<cpu::MMIOReadCallback>(ReadRegisterThunk),
|
reinterpret_cast<cpu::MMIOReadCallback>(ReadRegisterThunk),
|
||||||
reinterpret_cast<cpu::MMIOWriteCallback>(WriteRegisterThunk));
|
reinterpret_cast<cpu::MMIOWriteCallback>(WriteRegisterThunk));
|
||||||
|
|
||||||
// 60hz vsync timer.
|
// Frame limiter thread.
|
||||||
vsync_worker_running_ = true;
|
frame_limiter_worker_running_ = true;
|
||||||
vsync_worker_thread_ =
|
frame_limiter_worker_thread_ =
|
||||||
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
kernel::object_ref<kernel::XHostThread>(new kernel::XHostThread(
|
||||||
kernel_state_, 128 * 1024, 0,
|
kernel_state_, 128 * 1024, 0,
|
||||||
[this]() {
|
[this]() {
|
||||||
|
uint64_t normalized_framerate_limit =
|
||||||
|
std::max<uint64_t>(0, cvars::framerate_limit);
|
||||||
|
|
||||||
|
// If VSYNC is enabled, but frames are not limited,
|
||||||
|
// lock framerate at default value of 60
|
||||||
|
if (normalized_framerate_limit == 0 && cvars::vsync)
|
||||||
|
normalized_framerate_limit = 60;
|
||||||
|
|
||||||
const double vsync_duration_d =
|
const double vsync_duration_d =
|
||||||
cvars::vsync
|
cvars::vsync
|
||||||
? std::max<double>(
|
? std::max<double>(5.0,
|
||||||
5.0, 1000.0 / static_cast<double>(cvars::vsync_fps))
|
1000.0 / static_cast<double>(
|
||||||
|
normalized_framerate_limit))
|
||||||
: 1.0;
|
: 1.0;
|
||||||
uint64_t last_frame_time = Clock::QueryGuestTickCount();
|
uint64_t last_frame_time = Clock::QueryGuestTickCount();
|
||||||
// Sleep for 90% of the vblank duration, spin for 10%
|
// Sleep for 90% of the vblank duration, spin for 10%
|
||||||
const double duration_scalar = 0.90;
|
const double duration_scalar = 0.90;
|
||||||
|
|
||||||
while (vsync_worker_running_) {
|
while (frame_limiter_worker_running_) {
|
||||||
const uint64_t current_time = Clock::QueryGuestTickCount();
|
if (cvars::vsync) {
|
||||||
const uint64_t tick_freq = Clock::guest_tick_frequency();
|
const uint64_t current_time = Clock::QueryGuestTickCount();
|
||||||
const uint64_t time_delta = current_time - last_frame_time;
|
const uint64_t tick_freq = Clock::guest_tick_frequency();
|
||||||
const double elapsed_d =
|
const uint64_t time_delta = current_time - last_frame_time;
|
||||||
static_cast<double>(time_delta) /
|
const double elapsed_d =
|
||||||
(static_cast<double>(tick_freq) / 1000.0);
|
static_cast<double>(time_delta) /
|
||||||
if (elapsed_d >= vsync_duration_d) {
|
(static_cast<double>(tick_freq) / 1000.0);
|
||||||
last_frame_time = current_time;
|
if (elapsed_d >= vsync_duration_d) {
|
||||||
|
last_frame_time = current_time;
|
||||||
|
|
||||||
// TODO(disjtqz): should recalculate the remaining time to a
|
// TODO(disjtqz): should recalculate the remaining time to a
|
||||||
// vblank after MarkVblank, no idea how long the guest code
|
// vblank after MarkVblank, no idea how long the guest code
|
||||||
// normally takes
|
// normally takes
|
||||||
MarkVblank();
|
MarkVblank();
|
||||||
if (cvars::vsync) {
|
if (cvars::vsync) {
|
||||||
const uint64_t estimated_nanoseconds = static_cast<uint64_t>(
|
const uint64_t estimated_nanoseconds =
|
||||||
(vsync_duration_d * 1000000.0) *
|
static_cast<uint64_t>(
|
||||||
duration_scalar); // 1000 microseconds = 1 ms
|
(vsync_duration_d * 1000000.0) *
|
||||||
|
duration_scalar); // 1000 microseconds = 1 ms
|
||||||
|
|
||||||
threading::NanoSleep(estimated_nanoseconds);
|
threading::NanoSleep(estimated_nanoseconds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cvars::vsync) {
|
if (!cvars::vsync) {
|
||||||
xe::threading::Sleep(std::chrono::milliseconds(1));
|
MarkVblank();
|
||||||
|
if (normalized_framerate_limit > 0) {
|
||||||
|
// framerate_limit is over 0, vsync disabled
|
||||||
|
// - No VSYNC + limited frames defined by user
|
||||||
|
uint64_t framerate_limited_sleep_time =
|
||||||
|
1000000000 / normalized_framerate_limit;
|
||||||
|
xe::threading::NanoSleep(framerate_limited_sleep_time);
|
||||||
|
} else {
|
||||||
|
// framerate_limit is 0, vsync disabled
|
||||||
|
// - No VSYNC + unlimited frames
|
||||||
|
xe::threading::Sleep(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
kernel_state->GetIdleProcess()));
|
kernel_state->GetIdleProcess()));
|
||||||
// As we run vblank interrupts the debugger must be able to suspend us.
|
// As we run vblank interrupts the debugger must be able to suspend us.
|
||||||
vsync_worker_thread_->set_can_debugger_suspend(true);
|
frame_limiter_worker_thread_->set_can_debugger_suspend(true);
|
||||||
vsync_worker_thread_->set_name("GPU VSync");
|
frame_limiter_worker_thread_->set_name("GPU Frame limiter");
|
||||||
vsync_worker_thread_->Create();
|
frame_limiter_worker_thread_->Create();
|
||||||
vsync_worker_thread_->thread()->set_priority(
|
frame_limiter_worker_thread_->thread()->set_priority(
|
||||||
threading::ThreadPriority::kLowest);
|
threading::ThreadPriority::kLowest);
|
||||||
if (cvars::trace_gpu_stream) {
|
if (cvars::trace_gpu_stream) {
|
||||||
BeginTracing();
|
BeginTracing();
|
||||||
|
@ -164,10 +188,10 @@ void GraphicsSystem::Shutdown() {
|
||||||
command_processor_.reset();
|
command_processor_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vsync_worker_thread_) {
|
if (frame_limiter_worker_thread_) {
|
||||||
vsync_worker_running_ = false;
|
frame_limiter_worker_running_ = false;
|
||||||
vsync_worker_thread_->Wait(0, 0, 0, nullptr);
|
frame_limiter_worker_thread_->Wait(0, 0, 0, nullptr);
|
||||||
vsync_worker_thread_.reset();
|
frame_limiter_worker_thread_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (presenter_) {
|
if (presenter_) {
|
||||||
|
|
|
@ -109,8 +109,8 @@ class GraphicsSystem {
|
||||||
uint32_t interrupt_callback_ = 0;
|
uint32_t interrupt_callback_ = 0;
|
||||||
uint32_t interrupt_callback_data_ = 0;
|
uint32_t interrupt_callback_data_ = 0;
|
||||||
|
|
||||||
std::atomic<bool> vsync_worker_running_;
|
std::atomic<bool> frame_limiter_worker_running_;
|
||||||
kernel::object_ref<kernel::XHostThread> vsync_worker_thread_;
|
kernel::object_ref<kernel::XHostThread> frame_limiter_worker_thread_;
|
||||||
|
|
||||||
RegisterFile* register_file_;
|
RegisterFile* register_file_;
|
||||||
std::unique_ptr<CommandProcessor> command_processor_;
|
std::unique_ptr<CommandProcessor> command_processor_;
|
||||||
|
|
Loading…
Reference in New Issue