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.
This commit is contained in:
Vinicius Rangel 2024-09-20 19:06:07 -03:00
parent d708374f44
commit 5b7ee037ac
No known key found for this signature in database
GPG key ID: A5B154D904B761D9
5 changed files with 58 additions and 13 deletions

View file

@ -3,10 +3,12 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string>
#include <thread>
#include "common/error.h"
#include "common/logging/log.h"
#include "common/thread.h"
#include "ntapi.h"
#ifdef __APPLE__
#include <mach/mach.h>
#include <mach/mach_time.h>
@ -102,6 +104,16 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
SetThreadPriority(handle, windows_priority);
}
static void AccurateSleep(std::chrono::nanoseconds duration) {
LARGE_INTEGER interval{
.QuadPart = -1 * (duration.count() / 100u),
};
HANDLE timer = ::CreateWaitableTimer(NULL, TRUE, NULL);
SetWaitableTimer(timer, &interval, 0, NULL, NULL, 0);
WaitForSingleObject(timer, INFINITE);
::CloseHandle(timer);
}
#else
void SetCurrentThreadPriority(ThreadPriority new_priority) {
@ -122,6 +134,10 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
pthread_setschedparam(this_thread, scheduling_type, &params);
}
static void AccurateSleep(std::chrono::nanoseconds duration) {
std::this_thread::sleep_for(duration);
}
#endif
#ifdef _MSC_VER
@ -164,4 +180,22 @@ void SetCurrentThreadName(const char*) {
#endif
AccurateTimer::AccurateTimer(std::chrono::nanoseconds target_interval)
: target_interval(target_interval) {}
void AccurateTimer::Start() {
auto begin_sleep = std::chrono::high_resolution_clock::now();
if (total_wait.count() > 0) {
AccurateSleep(total_wait);
}
start_time = std::chrono::high_resolution_clock::now();
total_wait -= std::chrono::duration_cast<std::chrono::nanoseconds>(start_time - begin_sleep);
}
void AccurateTimer::End() {
auto now = std::chrono::high_resolution_clock::now();
total_wait +=
target_interval - std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time);
}
} // namespace Common

View file

@ -23,4 +23,18 @@ void SetCurrentThreadPriority(ThreadPriority new_priority);
void SetCurrentThreadName(const char* name);
class AccurateTimer {
std::chrono::nanoseconds target_interval{};
std::chrono::nanoseconds total_wait{};
std::chrono::high_resolution_clock::time_point start_time;
public:
explicit AccurateTimer(std::chrono::nanoseconds target_interval);
void Start();
void End();
};
} // namespace Common

View file

@ -161,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.
@ -199,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() {
@ -268,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()) {
@ -280,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;
}
}
@ -314,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);

View file

@ -38,7 +38,7 @@ static void DrawAdvanced() {
const auto& window = *ctx.CurrentWindow;
auto& draw_list = *window.DrawList;
Text("Frame time: %.3f ms (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate);
Text("Frame time: %.3f ms (%.1f FPS)", io.DeltaTime * 1000.0f, io.Framerate);
SeparatorText("Frame graph");
const float full_width = GetContentRegionAvail().x;
@ -69,7 +69,7 @@ static void DrawAdvanced() {
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * frame_graph_height;
ImU32 color;
if (dt_factor >= 0.992f) { // BLUE
if (dt_factor >= 0.95f) { // BLUE
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;