From 6595670a5d3d7930fb2c0b7e3f853b6422206dd5 Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Sun, 27 Jun 2021 06:56:16 +0530 Subject: [PATCH] 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. --- .gitignore | 1 + app/src/main/cpp/emu_jni.cpp | 23 ++++++++++++------- .../cpp/skyline/gpu/presentation_engine.cpp | 21 +++++++++++++---- .../cpp/skyline/gpu/presentation_engine.h | 4 +++- .../java/emu/skyline/EmulationActivity.kt | 5 ++-- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index a53b2445..2bc53e0b 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ build/ .idea/caches .idea/assetWizardSettings.xml .idea/runConfigurations.xml +.idea/deploymentTargetDropDown.xml # Mongo Explorer plugin .idea/**/mongoSettings.xml diff --git a/app/src/main/cpp/emu_jni.cpp b/app/src/main/cpp/emu_jni.cpp index 64832751..28184e12 100644 --- a/app/src/main/cpp/emu_jni.cpp +++ b/app/src/main/cpp/emu_jni.cpp @@ -19,15 +19,17 @@ #include "skyline/input.h" #include "skyline/kernel/types/KProcess.h" -skyline::i32 Fps; -skyline::i32 FrameTime; +jint Fps; //!< An approximation of the amount of frames being submitted every second +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 OsWeak; std::weak_ptr GpuWeak; std::weak_ptr InputWeak; // 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() { - const char* nameEnv = getenv("TZ"); + const char *nameEnv = getenv("TZ"); if (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) { 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"); @@ -133,10 +135,15 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_updatePerformanceSt fpsField = env->GetFieldID(clazz, "fps", "I"); env->SetIntField(thiz, fpsField, Fps); - static jfieldID frametimeField{}; - if (!frametimeField) - frametimeField = env->GetFieldID(clazz, "frametime", "F"); - env->SetFloatField(thiz, frametimeField, static_cast(FrameTime) / 100); + static jfieldID averageFrametimeField{}; + if (!averageFrametimeField) + averageFrametimeField = env->GetFieldID(clazz, "averageFrametime", "F"); + 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) { diff --git a/app/src/main/cpp/skyline/gpu/presentation_engine.cpp b/app/src/main/cpp/skyline/gpu/presentation_engine.cpp index ef404071..b3b28754 100644 --- a/app/src/main/cpp/skyline/gpu/presentation_engine.cpp +++ b/app/src/main/cpp/skyline/gpu/presentation_engine.cpp @@ -13,8 +13,9 @@ #include "native_window.h" #include "texture/format.h" -extern skyline::i32 Fps; -extern skyline::i32 FrameTime; +extern jint Fps; +extern jfloat AverageFrametimeMs; +extern jfloat AverageFrametimeDeviationMs; namespace skyline::gpu { using namespace service::hosbinder; @@ -39,11 +40,12 @@ namespace skyline::gpu { void PresentationEngine::ChoreographerCallback(long frameTimeNanos, PresentationEngine *engine) { u64 cycleLength{frameTimeNanos - engine->lastChoreographerTime}; - if (std::abs(static_cast(cycleLength - engine->refreshCycleDuration)) > (constant::NsInMillisecond / 2)) + if (std::abs(static_cast(cycleLength - engine->refreshCycleDuration)) > (constant::NsInMillisecond / 2)) { if (engine->window) engine->window->perform(engine->window, NATIVE_WINDOW_GET_REFRESH_CYCLE_DURATION, &engine->refreshCycleDuration); else engine->refreshCycleDuration = cycleLength; + } engine->lastChoreographerTime = frameTimeNanos; engine->vsyncEvent->Signal(); @@ -271,8 +273,17 @@ namespace skyline::gpu { if (frameTimestamp) { auto now{util::GetTimeNs()}; - FrameTime = static_cast((now - frameTimestamp) / 10000); // frametime / 100 is the real ms value, this is to retain the first two decimals - Fps = static_cast(constant::NsInSecond / (now - frameTimestamp)); + 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 + + u64 currentFrametime{now - frameTimestamp}; + averageFrametimeNs = (((sampleWeight - 1) * averageFrametimeNs) / sampleWeight) + (currentFrametime / sampleWeight); + AverageFrametimeMs = static_cast(averageFrametimeNs) / constant::NsInMillisecond; + + i64 currentFrametimeDeviation{std::abs(static_cast(averageFrametimeNs - currentFrametime))}; + averageFrametimeDeviationNs = (((sampleWeight - 1) * averageFrametimeDeviationNs) / sampleWeight) + (currentFrametimeDeviation / sampleWeight); + AverageFrametimeDeviationMs = static_cast(averageFrametimeDeviationNs) / constant::NsInMillisecond; + + Fps = std::round(static_cast(constant::NsInSecond) / averageFrametimeNs); TRACE_EVENT_INSTANT("gpu", "Present", presentationTrack, "FrameTimeNs", now - frameTimestamp, "Fps", Fps); diff --git a/app/src/main/cpp/skyline/gpu/presentation_engine.h b/app/src/main/cpp/skyline/gpu/presentation_engine.h index afc6df3d..d1b7994e 100644 --- a/app/src/main/cpp/skyline/gpu/presentation_engine.h +++ b/app/src/main/cpp/skyline/gpu/presentation_engine.h @@ -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 std::array, 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 std::thread choreographerThread; //!< A thread for signalling the V-Sync event using AChoreographer diff --git a/app/src/main/java/emu/skyline/EmulationActivity.kt b/app/src/main/java/emu/skyline/EmulationActivity.kt index a53fad25..d472109a 100644 --- a/app/src/main/java/emu/skyline/EmulationActivity.kt +++ b/app/src/main/java/emu/skyline/EmulationActivity.kt @@ -86,7 +86,8 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo private external fun setSurface(surface : Surface?) : Boolean 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 @@ -204,7 +205,7 @@ class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback, View.OnTo postDelayed(object : Runnable { override fun run() { updatePerformanceStatistics() - text = "$fps FPS\n${frametime}ms" + text = "$fps FPS\n${"%.1f".format(averageFrametime)}±${"%.2f".format(averageFrametimeDeviation)}ms" postDelayed(this, 250) } }, 250)