mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-22 04:24:44 +00:00
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.
This commit is contained in:
parent
d708374f44
commit
5b7ee037ac
5 changed files with 58 additions and 13 deletions
|
@ -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, ¶ms);
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue