mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-25 22:54:17 +01:00
Rework Performance Statistics
We used instantaneous values for FPS previously which led to a lot of variation in it and the inability to determine a proper FPS value due to constant fluctuations. All FPS values are now averaged to allow reading out a stable value and a deviation statistic has been added for the frame-time to judge judder and frame-pacing which allows for a significantly better measure of overall performance. The formatting for all the floating-point numbers is now fixed-point to prevent shifting of position due to decimal digits becoming 0.
This commit is contained in:
parent
ce804254ab
commit
6595670a5d
1
.gitignore
vendored
1
.gitignore
vendored
@ -51,6 +51,7 @@ build/
|
|||||||
.idea/caches
|
.idea/caches
|
||||||
.idea/assetWizardSettings.xml
|
.idea/assetWizardSettings.xml
|
||||||
.idea/runConfigurations.xml
|
.idea/runConfigurations.xml
|
||||||
|
.idea/deploymentTargetDropDown.xml
|
||||||
|
|
||||||
# Mongo Explorer plugin
|
# Mongo Explorer plugin
|
||||||
.idea/**/mongoSettings.xml
|
.idea/**/mongoSettings.xml
|
||||||
|
@ -19,15 +19,17 @@
|
|||||||
#include "skyline/input.h"
|
#include "skyline/input.h"
|
||||||
#include "skyline/kernel/types/KProcess.h"
|
#include "skyline/kernel/types/KProcess.h"
|
||||||
|
|
||||||
skyline::i32 Fps;
|
jint Fps; //!< An approximation of the amount of frames being submitted every second
|
||||||
skyline::i32 FrameTime;
|
jfloat AverageFrametimeMs; //!< The average time it takes for a frame to be rendered and presented in milliseconds
|
||||||
|
jfloat AverageFrametimeDeviationMs; //!< The average deviation of the average frametimes in milliseconds
|
||||||
|
|
||||||
std::weak_ptr<skyline::kernel::OS> OsWeak;
|
std::weak_ptr<skyline::kernel::OS> OsWeak;
|
||||||
std::weak_ptr<skyline::gpu::GPU> GpuWeak;
|
std::weak_ptr<skyline::gpu::GPU> GpuWeak;
|
||||||
std::weak_ptr<skyline::input::Input> InputWeak;
|
std::weak_ptr<skyline::input::Input> InputWeak;
|
||||||
|
|
||||||
// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=43;drc=master;bpv=1;bpt=1
|
// https://cs.android.com/android/platform/superproject/+/master:bionic/libc/tzcode/bionic.cpp;l=43;drc=master;bpv=1;bpt=1
|
||||||
static std::string GetTimeZoneName() {
|
static std::string GetTimeZoneName() {
|
||||||
const char* nameEnv = getenv("TZ");
|
const char *nameEnv = getenv("TZ");
|
||||||
if (nameEnv)
|
if (nameEnv)
|
||||||
return std::string(nameEnv);
|
return std::string(nameEnv);
|
||||||
|
|
||||||
@ -52,7 +54,7 @@ static std::string GetTimeZoneName() {
|
|||||||
|
|
||||||
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring, jobject assetManager) {
|
extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(JNIEnv *env, jobject instance, jstring romUriJstring, jint romType, jint romFd, jint preferenceFd, jstring appFilesPathJstring, jobject assetManager) {
|
||||||
skyline::signal::ScopedStackBlocker stackBlocker; // We do not want anything to unwind past JNI code as there are invalid stack frames which can lead to a segmentation fault
|
skyline::signal::ScopedStackBlocker stackBlocker; // We do not want anything to unwind past JNI code as there are invalid stack frames which can lead to a segmentation fault
|
||||||
Fps = FrameTime = 0;
|
Fps = AverageFrametimeMs = AverageFrametimeDeviationMs = 0;
|
||||||
|
|
||||||
pthread_setname_np(pthread_self(), "EmuMain");
|
pthread_setname_np(pthread_self(), "EmuMain");
|
||||||
|
|
||||||
@ -133,10 +135,15 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_updatePerformanceSt
|
|||||||
fpsField = env->GetFieldID(clazz, "fps", "I");
|
fpsField = env->GetFieldID(clazz, "fps", "I");
|
||||||
env->SetIntField(thiz, fpsField, Fps);
|
env->SetIntField(thiz, fpsField, Fps);
|
||||||
|
|
||||||
static jfieldID frametimeField{};
|
static jfieldID averageFrametimeField{};
|
||||||
if (!frametimeField)
|
if (!averageFrametimeField)
|
||||||
frametimeField = env->GetFieldID(clazz, "frametime", "F");
|
averageFrametimeField = env->GetFieldID(clazz, "averageFrametime", "F");
|
||||||
env->SetFloatField(thiz, frametimeField, static_cast<float>(FrameTime) / 100);
|
env->SetFloatField(thiz, averageFrametimeField, AverageFrametimeMs);
|
||||||
|
|
||||||
|
static jfieldID averageFrametimeDeviationField{};
|
||||||
|
if (!averageFrametimeDeviationField)
|
||||||
|
averageFrametimeDeviationField = env->GetFieldID(clazz, "averageFrametimeDeviation", "F");
|
||||||
|
env->SetFloatField(thiz, averageFrametimeDeviationField, AverageFrametimeDeviationMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
|
extern "C" JNIEXPORT void JNICALL Java_emu_skyline_EmulationActivity_setController(JNIEnv *, jobject, jint index, jint type, jint partnerIndex) {
|
||||||
|
@ -13,8 +13,9 @@
|
|||||||
#include "native_window.h"
|
#include "native_window.h"
|
||||||
#include "texture/format.h"
|
#include "texture/format.h"
|
||||||
|
|
||||||
extern skyline::i32 Fps;
|
extern jint Fps;
|
||||||
extern skyline::i32 FrameTime;
|
extern jfloat AverageFrametimeMs;
|
||||||
|
extern jfloat AverageFrametimeDeviationMs;
|
||||||
|
|
||||||
namespace skyline::gpu {
|
namespace skyline::gpu {
|
||||||
using namespace service::hosbinder;
|
using namespace service::hosbinder;
|
||||||
@ -39,11 +40,12 @@ namespace skyline::gpu {
|
|||||||
|
|
||||||
void PresentationEngine::ChoreographerCallback(long frameTimeNanos, PresentationEngine *engine) {
|
void PresentationEngine::ChoreographerCallback(long frameTimeNanos, PresentationEngine *engine) {
|
||||||
u64 cycleLength{frameTimeNanos - engine->lastChoreographerTime};
|
u64 cycleLength{frameTimeNanos - engine->lastChoreographerTime};
|
||||||
if (std::abs(static_cast<i64>(cycleLength - engine->refreshCycleDuration)) > (constant::NsInMillisecond / 2))
|
if (std::abs(static_cast<i64>(cycleLength - engine->refreshCycleDuration)) > (constant::NsInMillisecond / 2)) {
|
||||||
if (engine->window)
|
if (engine->window)
|
||||||
engine->window->perform(engine->window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION, &engine->refreshCycleDuration);
|
engine->window->perform(engine->window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION, &engine->refreshCycleDuration);
|
||||||
else
|
else
|
||||||
engine->refreshCycleDuration = cycleLength;
|
engine->refreshCycleDuration = cycleLength;
|
||||||
|
}
|
||||||
|
|
||||||
engine->lastChoreographerTime = frameTimeNanos;
|
engine->lastChoreographerTime = frameTimeNanos;
|
||||||
engine->vsyncEvent->Signal();
|
engine->vsyncEvent->Signal();
|
||||||
@ -271,8 +273,17 @@ namespace skyline::gpu {
|
|||||||
|
|
||||||
if (frameTimestamp) {
|
if (frameTimestamp) {
|
||||||
auto now{util::GetTimeNs()};
|
auto now{util::GetTimeNs()};
|
||||||
FrameTime = static_cast<u32>((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals
|
auto sampleWeight{swapInterval ? constant::NsInSecond / (refreshCycleDuration * swapInterval) : 10}; //!< The weight of each sample in calculating the average, we arbitrarily average 10 samples for unlocked FPS
|
||||||
Fps = static_cast<u16>(constant::NsInSecond / (now - frameTimestamp));
|
|
||||||
|
u64 currentFrametime{now - frameTimestamp};
|
||||||
|
averageFrametimeNs = (((sampleWeight - 1) * averageFrametimeNs) / sampleWeight) + (currentFrametime / sampleWeight);
|
||||||
|
AverageFrametimeMs = static_cast<jfloat>(averageFrametimeNs) / constant::NsInMillisecond;
|
||||||
|
|
||||||
|
i64 currentFrametimeDeviation{std::abs(static_cast<i64>(averageFrametimeNs - currentFrametime))};
|
||||||
|
averageFrametimeDeviationNs = (((sampleWeight - 1) * averageFrametimeDeviationNs) / sampleWeight) + (currentFrametimeDeviation / sampleWeight);
|
||||||
|
AverageFrametimeDeviationMs = static_cast<jfloat>(averageFrametimeDeviationNs) / constant::NsInMillisecond;
|
||||||
|
|
||||||
|
Fps = std::round(static_cast<float>(constant::NsInSecond) / averageFrametimeNs);
|
||||||
|
|
||||||
TRACE_EVENT_INSTANT("gpu", "Present", presentationTrack, "FrameTimeNs", now - frameTimestamp, "Fps", Fps);
|
TRACE_EVENT_INSTANT("gpu", "Present", presentationTrack, "FrameTimeNs", now - frameTimestamp, "Fps", Fps);
|
||||||
|
|
||||||
|
@ -40,7 +40,9 @@ namespace skyline::gpu {
|
|||||||
static constexpr size_t MaxSwapchainImageCount{6}; //!< The maximum amount of swapchain textures, this affects the amount of images that can be in the swapchain
|
static constexpr size_t MaxSwapchainImageCount{6}; //!< The maximum amount of swapchain textures, this affects the amount of images that can be in the swapchain
|
||||||
std::array<std::shared_ptr<Texture>, MaxSwapchainImageCount> images; //!< All the swapchain textures in the same order as supplied by the host swapchain
|
std::array<std::shared_ptr<Texture>, MaxSwapchainImageCount> images; //!< All the swapchain textures in the same order as supplied by the host swapchain
|
||||||
|
|
||||||
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown
|
u64 frameTimestamp{}; //!< The timestamp of the last frame being shown in nanoseconds
|
||||||
|
u64 averageFrametimeNs{}; //!< The average time between frames in nanoseconds
|
||||||
|
u64 averageFrametimeDeviationNs{}; //!< The average deviation of frametimes in nanoseconds
|
||||||
perfetto::Track presentationTrack; //!< Perfetto track used for presentation events
|
perfetto::Track presentationTrack; //!< Perfetto track used for presentation events
|
||||||
|
|
||||||
std::thread choreographerThread; //!< A thread for signalling the V-Sync event using AChoreographer
|
std::thread choreographerThread; //!< A thread for signalling the V-Sync event using AChoreographer
|
||||||
|
@ -86,7 +86,8 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
|||||||
private external fun setSurface(surface : Surface?) : Boolean
|
private external fun setSurface(surface : Surface?) : Boolean
|
||||||
|
|
||||||
var fps : Int = 0
|
var fps : Int = 0
|
||||||
var frametime : Float = 0.0f
|
var averageFrametime : Float = 0.0f
|
||||||
|
var averageFrametimeDeviation : Float = 0.0f
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the current performance statistics into [fps] and [frametime] fields
|
* Writes the current performance statistics into [fps] and [frametime] fields
|
||||||
@ -204,7 +205,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo
|
|||||||
postDelayed(object : Runnable {
|
postDelayed(object : Runnable {
|
||||||
override fun run() {
|
override fun run() {
|
||||||
updatePerformanceStatistics()
|
updatePerformanceStatistics()
|
||||||
text = "$fps FPS\n${frametime}ms"
|
text = "$fps FPS\n${"%.1f".format(averageFrametime)}±${"%.2f".format(averageFrametimeDeviation)}ms"
|
||||||
postDelayed(this, 250)
|
postDelayed(this, 250)
|
||||||
}
|
}
|
||||||
}, 250)
|
}, 250)
|
||||||
|
Loading…
Reference in New Issue
Block a user