Frame graph + Precise 60 fps timing (#998)

* video info: add frame graph

Toggle advanced info with CTRL+F10.
Also fixed imgui using gamepad for nav in wrong situations

* 60fps!

Implemented a timer that accumulates the time spent sleeping and sleeps for the remaining time.
Also measure entire PresentThread time instead of just the time spent in Flip.

* sceKernelGettimeofday: replace chrono by win32 api. Better performance

bb uses this function too much. Consuming almost 30% of cpu time
This commit is contained in:
Vinicius Rangel 2024-09-23 12:43:51 -03:00 committed by GitHub
parent a016792371
commit 5a8e8f5936
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 183 additions and 26 deletions

View file

@ -147,13 +147,20 @@ int PS4_SYSV_ABI sceKernelGettimeofday(OrbisKernelTimeval* tp) {
}
#ifdef _WIN64
auto now = std::chrono::system_clock::now();
auto duration = now.time_since_epoch();
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
auto microsecs = std::chrono::duration_cast<std::chrono::microseconds>(duration - seconds);
FILETIME filetime;
GetSystemTimeAsFileTime(&filetime);
tp->tv_sec = seconds.count();
tp->tv_usec = microsecs.count();
constexpr u64 UNIX_TIME_START = 0x295E9648864000;
constexpr u64 TICKS_PER_SECOND = 1000000;
u64 ticks = filetime.dwHighDateTime;
ticks <<= 32;
ticks |= filetime.dwLowDateTime;
ticks /= 10;
ticks -= UNIX_TIME_START;
tp->tv_sec = ticks / TICKS_PER_SECOND;
tp->tv_usec = ticks % TICKS_PER_SECOND;
#else
timeval tv;
gettimeofday(&tv, nullptr);

View file

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <imgui.h>
#include <pthread.h>
#include "common/assert.h"
@ -160,9 +161,7 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) {
return ORBIS_OK;
}
std::chrono::microseconds VideoOutDriver::Flip(const Request& req) {
const auto start = std::chrono::high_resolution_clock::now();
void VideoOutDriver::Flip(const Request& req) {
// Whatever the game is rendering show splash if it is active
if (!renderer->ShowSplash(req.frame)) {
// Present the frame.
@ -198,9 +197,6 @@ std::chrono::microseconds VideoOutDriver::Flip(const Request& req) {
port->buffer_labels[req.index] = 0;
port->SignalVoLabel();
}
const auto end = std::chrono::high_resolution_clock::now();
return std::chrono::duration_cast<std::chrono::microseconds>(end - start);
}
void VideoOutDriver::DrawBlankFrame() {
@ -267,6 +263,8 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
Common::SetCurrentThreadName("PresentThread");
Common::SetCurrentThreadRealtime(vblank_period);
Common::AccurateTimer timer{vblank_period};
const auto receive_request = [this] -> Request {
std::scoped_lock lk{mutex};
if (!requests.empty()) {
@ -279,20 +277,18 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
auto delay = std::chrono::microseconds{0};
while (!token.stop_requested()) {
// Sleep for most of the vblank duration.
std::this_thread::sleep_for(vblank_period - delay);
timer.Start();
// Check if it's time to take a request.
auto& vblank_status = main_port.vblank_status;
if (vblank_status.count % (main_port.flip_rate + 1) == 0) {
const auto request = receive_request();
if (!request) {
delay = std::chrono::microseconds{0};
if (!main_port.is_open) {
DrawBlankFrame();
}
} else {
delay = Flip(request);
Flip(request);
FRAME_END;
}
}
@ -313,6 +309,8 @@ void VideoOutDriver::PresentThread(std::stop_token token) {
Kernel::SceKernelEvent::Filter::VideoOut, nullptr);
}
}
timer.End();
}
}

View file

@ -101,7 +101,7 @@ private:
}
};
std::chrono::microseconds Flip(const Request& req);
void Flip(const Request& req);
void DrawBlankFrame(); // Used when there is no flip request to keep ImGui up to date
void SubmitFlipInternal(VideoOutPort* port, s32 index, s64 flip_arg, bool is_eop = false);
void PresentThread(std::stop_token token);