summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/core.cpp25
-rw-r--r--src/core/file_sys/submission_package.cpp26
-rw-r--r--src/core/loader/nsp.cpp27
-rw-r--r--src/core/memory.cpp9
-rw-r--r--src/core/perf_stats.cpp47
-rw-r--r--src/core/perf_stats.h21
-rw-r--r--src/core/settings.h1
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h4
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h2
12 files changed, 143 insertions, 28 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 3d0978cbf..9ab174de2 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -160,10 +160,6 @@ struct System::Impl {
160 160
161 LOG_DEBUG(Core, "Initialized OK"); 161 LOG_DEBUG(Core, "Initialized OK");
162 162
163 // Reset counters and set time origin to current frame
164 GetAndResetPerfStats();
165 perf_stats.BeginSystemFrame();
166
167 return ResultStatus::Success; 163 return ResultStatus::Success;
168 } 164 }
169 165
@@ -206,6 +202,16 @@ struct System::Impl {
206 main_process->Run(load_parameters->main_thread_priority, 202 main_process->Run(load_parameters->main_thread_priority,
207 load_parameters->main_thread_stack_size); 203 load_parameters->main_thread_stack_size);
208 204
205 u64 title_id{0};
206 if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
207 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
208 static_cast<u32>(load_result));
209 }
210 perf_stats = std::make_unique<PerfStats>(title_id);
211 // Reset counters and set time origin to current frame
212 GetAndResetPerfStats();
213 perf_stats->BeginSystemFrame();
214
209 status = ResultStatus::Success; 215 status = ResultStatus::Success;
210 return status; 216 return status;
211 } 217 }
@@ -219,6 +225,8 @@ struct System::Impl {
219 perf_results.game_fps); 225 perf_results.game_fps);
220 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime", 226 telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
221 perf_results.frametime * 1000.0); 227 perf_results.frametime * 1000.0);
228 telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
229 perf_stats->GetMeanFrametime());
222 230
223 is_powered_on = false; 231 is_powered_on = false;
224 232
@@ -229,6 +237,7 @@ struct System::Impl {
229 service_manager.reset(); 237 service_manager.reset();
230 cheat_engine.reset(); 238 cheat_engine.reset();
231 telemetry_session.reset(); 239 telemetry_session.reset();
240 perf_stats.reset();
232 gpu_core.reset(); 241 gpu_core.reset();
233 242
234 // Close all CPU/threading state 243 // Close all CPU/threading state
@@ -286,7 +295,7 @@ struct System::Impl {
286 } 295 }
287 296
288 PerfStatsResults GetAndResetPerfStats() { 297 PerfStatsResults GetAndResetPerfStats() {
289 return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs()); 298 return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
290 } 299 }
291 300
292 Timing::CoreTiming core_timing; 301 Timing::CoreTiming core_timing;
@@ -327,7 +336,7 @@ struct System::Impl {
327 ResultStatus status = ResultStatus::Success; 336 ResultStatus status = ResultStatus::Success;
328 std::string status_details = ""; 337 std::string status_details = "";
329 338
330 Core::PerfStats perf_stats; 339 std::unique_ptr<Core::PerfStats> perf_stats;
331 Core::FrameLimiter frame_limiter; 340 Core::FrameLimiter frame_limiter;
332}; 341};
333 342
@@ -480,11 +489,11 @@ const Timing::CoreTiming& System::CoreTiming() const {
480} 489}
481 490
482Core::PerfStats& System::GetPerfStats() { 491Core::PerfStats& System::GetPerfStats() {
483 return impl->perf_stats; 492 return *impl->perf_stats;
484} 493}
485 494
486const Core::PerfStats& System::GetPerfStats() const { 495const Core::PerfStats& System::GetPerfStats() const {
487 return impl->perf_stats; 496 return *impl->perf_stats;
488} 497}
489 498
490Core::FrameLimiter& System::FrameLimiter() { 499Core::FrameLimiter& System::FrameLimiter() {
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 8b3b14e25..730221fd6 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -14,6 +14,7 @@
14#include "core/file_sys/content_archive.h" 14#include "core/file_sys/content_archive.h"
15#include "core/file_sys/nca_metadata.h" 15#include "core/file_sys/nca_metadata.h"
16#include "core/file_sys/partition_filesystem.h" 16#include "core/file_sys/partition_filesystem.h"
17#include "core/file_sys/program_metadata.h"
17#include "core/file_sys/submission_package.h" 18#include "core/file_sys/submission_package.h"
18#include "core/loader/loader.h" 19#include "core/loader/loader.h"
19 20
@@ -78,6 +79,10 @@ Loader::ResultStatus NSP::GetStatus() const {
78} 79}
79 80
80Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { 81Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
82 if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
83 return Loader::ResultStatus::Success;
84 }
85
81 const auto iter = program_status.find(title_id); 86 const auto iter = program_status.find(title_id);
82 if (iter == program_status.end()) 87 if (iter == program_status.end())
83 return Loader::ResultStatus::ErrorNSPMissingProgramNCA; 88 return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
@@ -85,12 +90,29 @@ Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
85} 90}
86 91
87u64 NSP::GetFirstTitleID() const { 92u64 NSP::GetFirstTitleID() const {
93 if (IsExtractedType()) {
94 return GetProgramTitleID();
95 }
96
88 if (program_status.empty()) 97 if (program_status.empty())
89 return 0; 98 return 0;
90 return program_status.begin()->first; 99 return program_status.begin()->first;
91} 100}
92 101
93u64 NSP::GetProgramTitleID() const { 102u64 NSP::GetProgramTitleID() const {
103 if (IsExtractedType()) {
104 if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
105 return 0;
106 }
107
108 ProgramMetadata meta;
109 if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
110 return meta.GetTitleID();
111 } else {
112 return 0;
113 }
114 }
115
94 const auto out = GetFirstTitleID(); 116 const auto out = GetFirstTitleID();
95 if ((out & 0x800) == 0) 117 if ((out & 0x800) == 0)
96 return out; 118 return out;
@@ -102,6 +124,10 @@ u64 NSP::GetProgramTitleID() const {
102} 124}
103 125
104std::vector<u64> NSP::GetTitleIDs() const { 126std::vector<u64> NSP::GetTitleIDs() const {
127 if (IsExtractedType()) {
128 return {GetProgramTitleID()};
129 }
130
105 std::vector<u64> out; 131 std::vector<u64> out;
106 out.reserve(ncas.size()); 132 out.reserve(ncas.size());
107 for (const auto& kv : ncas) 133 for (const auto& kv : ncas)
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index b1171ce65..35c82c99d 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -26,20 +26,18 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
26 26
27 if (nsp->GetStatus() != ResultStatus::Success) 27 if (nsp->GetStatus() != ResultStatus::Success)
28 return; 28 return;
29 if (nsp->IsExtractedType())
30 return;
31
32 const auto control_nca =
33 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
34 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
35 return;
36
37 std::tie(nacp_file, icon_file) =
38 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
39 29
40 if (nsp->IsExtractedType()) { 30 if (nsp->IsExtractedType()) {
41 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS()); 31 secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
42 } else { 32 } else {
33 const auto control_nca =
34 nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
35 if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
36 return;
37
38 std::tie(nacp_file, icon_file) =
39 FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
40
43 if (title_id == 0) 41 if (title_id == 0)
44 return; 42 return;
45 43
@@ -56,11 +54,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
56 if (nsp.GetStatus() == ResultStatus::Success) { 54 if (nsp.GetStatus() == ResultStatus::Success) {
57 // Extracted Type case 55 // Extracted Type case
58 if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr && 56 if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
59 FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) { 57 FileSys::IsDirectoryExeFS(nsp.GetExeFS())) {
60 return FileType::NSP; 58 return FileType::NSP;
61 } 59 }
62 60
63 // Non-Ectracted Type case 61 // Non-Extracted Type case
64 if (!nsp.IsExtractedType() && 62 if (!nsp.IsExtractedType() &&
65 nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && 63 nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
66 AppLoader_NCA::IdentifyType(nsp.GetNCAFile( 64 AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
@@ -77,7 +75,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
77 return {ResultStatus::ErrorAlreadyLoaded, {}}; 75 return {ResultStatus::ErrorAlreadyLoaded, {}};
78 } 76 }
79 77
80 if (title_id == 0) { 78 if (!nsp->IsExtractedType() && title_id == 0) {
81 return {ResultStatus::ErrorNSPMissingProgramNCA, {}}; 79 return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
82 } 80 }
83 81
@@ -91,7 +89,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
91 return {nsp_program_status, {}}; 89 return {nsp_program_status, {}};
92 } 90 }
93 91
94 if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { 92 if (!nsp->IsExtractedType() &&
93 nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
95 if (!Core::Crypto::KeyManager::KeyFileExists(false)) { 94 if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
96 return {ResultStatus::ErrorMissingProductionKeyFile, {}}; 95 return {ResultStatus::ErrorMissingProductionKeyFile, {}};
97 } 96 }
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 8555691c0..9e030789d 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -43,8 +43,13 @@ static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* me
43 43
44 // During boot, current_page_table might not be set yet, in which case we need not flush 44 // During boot, current_page_table might not be set yet, in which case we need not flush
45 if (Core::System::GetInstance().IsPoweredOn()) { 45 if (Core::System::GetInstance().IsPoweredOn()) {
46 Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS, 46 auto& gpu = Core::System::GetInstance().GPU();
47 size * PAGE_SIZE); 47 for (u64 i = 0; i < size; i++) {
48 const auto page = base + i;
49 if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
50 gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
51 }
52 }
48 } 53 }
49 54
50 VAddr end = base + size; 55 VAddr end = base + size;
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp
index 4afd6c8a3..d2c69d1a0 100644
--- a/src/core/perf_stats.cpp
+++ b/src/core/perf_stats.cpp
@@ -4,8 +4,14 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <chrono> 6#include <chrono>
7#include <iterator>
7#include <mutex> 8#include <mutex>
9#include <numeric>
10#include <sstream>
8#include <thread> 11#include <thread>
12#include <fmt/chrono.h>
13#include <fmt/format.h>
14#include "common/file_util.h"
9#include "common/math_util.h" 15#include "common/math_util.h"
10#include "core/perf_stats.h" 16#include "core/perf_stats.h"
11#include "core/settings.h" 17#include "core/settings.h"
@@ -15,8 +21,31 @@ using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>;
15using std::chrono::duration_cast; 21using std::chrono::duration_cast;
16using std::chrono::microseconds; 22using std::chrono::microseconds;
17 23
24// Purposefully ignore the first five frames, as there's a significant amount of overhead in
25// booting that we shouldn't account for
26constexpr std::size_t IgnoreFrames = 5;
27
18namespace Core { 28namespace Core {
19 29
30PerfStats::PerfStats(u64 title_id) : title_id(title_id) {}
31
32PerfStats::~PerfStats() {
33 if (!Settings::values.record_frame_times || title_id == 0) {
34 return;
35 }
36
37 const std::time_t t = std::time(nullptr);
38 std::ostringstream stream;
39 std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
40 std::ostream_iterator<double>(stream, "\n"));
41 const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
42 // %F Date format expanded is "%Y-%m-%d"
43 const std::string filename =
44 fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
45 FileUtil::IOFile file(filename, "w");
46 file.WriteString(stream.str());
47}
48
20void PerfStats::BeginSystemFrame() { 49void PerfStats::BeginSystemFrame() {
21 std::lock_guard lock{object_mutex}; 50 std::lock_guard lock{object_mutex};
22 51
@@ -27,7 +56,12 @@ void PerfStats::EndSystemFrame() {
27 std::lock_guard lock{object_mutex}; 56 std::lock_guard lock{object_mutex};
28 57
29 auto frame_end = Clock::now(); 58 auto frame_end = Clock::now();
30 accumulated_frametime += frame_end - frame_begin; 59 const auto frame_time = frame_end - frame_begin;
60 if (current_index < perf_history.size()) {
61 perf_history[current_index++] =
62 std::chrono::duration<double, std::milli>(frame_time).count();
63 }
64 accumulated_frametime += frame_time;
31 system_frames += 1; 65 system_frames += 1;
32 66
33 previous_frame_length = frame_end - previous_frame_end; 67 previous_frame_length = frame_end - previous_frame_end;
@@ -40,6 +74,17 @@ void PerfStats::EndGameFrame() {
40 game_frames += 1; 74 game_frames += 1;
41} 75}
42 76
77double PerfStats::GetMeanFrametime() {
78 std::lock_guard lock{object_mutex};
79
80 if (current_index <= IgnoreFrames) {
81 return 0;
82 }
83 const double sum = std::accumulate(perf_history.begin() + IgnoreFrames,
84 perf_history.begin() + current_index, 0);
85 return sum / (current_index - IgnoreFrames);
86}
87
43PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) { 88PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
44 std::lock_guard lock{object_mutex}; 89 std::lock_guard lock{object_mutex};
45 90
diff --git a/src/core/perf_stats.h b/src/core/perf_stats.h
index 222ac1a63..d9a64f072 100644
--- a/src/core/perf_stats.h
+++ b/src/core/perf_stats.h
@@ -4,7 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
7#include <chrono> 8#include <chrono>
9#include <cstddef>
8#include <mutex> 10#include <mutex>
9#include "common/common_types.h" 11#include "common/common_types.h"
10 12
@@ -27,6 +29,10 @@ struct PerfStatsResults {
27 */ 29 */
28class PerfStats { 30class PerfStats {
29public: 31public:
32 explicit PerfStats(u64 title_id);
33
34 ~PerfStats();
35
30 using Clock = std::chrono::high_resolution_clock; 36 using Clock = std::chrono::high_resolution_clock;
31 37
32 void BeginSystemFrame(); 38 void BeginSystemFrame();
@@ -36,13 +42,26 @@ public:
36 PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us); 42 PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
37 43
38 /** 44 /**
45 * Returns the Arthimetic Mean of all frametime values stored in the performance history.
46 */
47 double GetMeanFrametime();
48
49 /**
39 * Gets the ratio between walltime and the emulated time of the previous system frame. This is 50 * Gets the ratio between walltime and the emulated time of the previous system frame. This is
40 * useful for scaling inputs or outputs moving between the two time domains. 51 * useful for scaling inputs or outputs moving between the two time domains.
41 */ 52 */
42 double GetLastFrameTimeScale(); 53 double GetLastFrameTimeScale();
43 54
44private: 55private:
45 std::mutex object_mutex; 56 std::mutex object_mutex{};
57
58 /// Title ID for the game that is running. 0 if there is no game running yet
59 u64 title_id{0};
60 /// Current index for writing to the perf_history array
61 std::size_t current_index{0};
62 /// Stores an hour of historical frametime data useful for processing and tracking performance
63 /// regressions with code changes.
64 std::array<double, 216000> perf_history = {};
46 65
47 /// Point when the cumulative counters were reset 66 /// Point when the cumulative counters were reset
48 Clock::time_point reset_point = Clock::now(); 67 Clock::time_point reset_point = Clock::now();
diff --git a/src/core/settings.h b/src/core/settings.h
index 6638ce8f9..d4b70ec4c 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -409,6 +409,7 @@ struct Values {
409 float volume; 409 float volume;
410 410
411 // Debugging 411 // Debugging
412 bool record_frame_times;
412 bool use_gdbstub; 413 bool use_gdbstub;
413 u16 gdbstub_port; 414 u16 gdbstub_port;
414 std::string program_args; 415 std::string program_args;
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index ea77dd211..9ed738171 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -145,7 +145,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
145 case Tegra::Texture::TextureMipmapFilter::None: 145 case Tegra::Texture::TextureMipmapFilter::None:
146 return GL_LINEAR; 146 return GL_LINEAR;
147 case Tegra::Texture::TextureMipmapFilter::Nearest: 147 case Tegra::Texture::TextureMipmapFilter::Nearest:
148 return GL_NEAREST_MIPMAP_LINEAR; 148 return GL_LINEAR_MIPMAP_NEAREST;
149 case Tegra::Texture::TextureMipmapFilter::Linear: 149 case Tegra::Texture::TextureMipmapFilter::Linear:
150 return GL_LINEAR_MIPMAP_LINEAR; 150 return GL_LINEAR_MIPMAP_LINEAR;
151 } 151 }
@@ -157,7 +157,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
157 case Tegra::Texture::TextureMipmapFilter::Nearest: 157 case Tegra::Texture::TextureMipmapFilter::Nearest:
158 return GL_NEAREST_MIPMAP_NEAREST; 158 return GL_NEAREST_MIPMAP_NEAREST;
159 case Tegra::Texture::TextureMipmapFilter::Linear: 159 case Tegra::Texture::TextureMipmapFilter::Linear:
160 return GL_LINEAR_MIPMAP_NEAREST; 160 return GL_NEAREST_MIPMAP_LINEAR;
161 } 161 }
162 } 162 }
163 } 163 }
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index f594106bf..3f54f54fb 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -466,6 +466,9 @@ void Config::ReadDataStorageValues() {
466void Config::ReadDebuggingValues() { 466void Config::ReadDebuggingValues() {
467 qt_config->beginGroup(QStringLiteral("Debugging")); 467 qt_config->beginGroup(QStringLiteral("Debugging"));
468 468
469 // Intentionally not using the QT default setting as this is intended to be changed in the ini
470 Settings::values.record_frame_times =
471 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
469 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); 472 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
470 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); 473 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
471 Settings::values.program_args = 474 Settings::values.program_args =
@@ -879,6 +882,8 @@ void Config::SaveDataStorageValues() {
879void Config::SaveDebuggingValues() { 882void Config::SaveDebuggingValues() {
880 qt_config->beginGroup(QStringLiteral("Debugging")); 883 qt_config->beginGroup(QStringLiteral("Debugging"));
881 884
885 // Intentionally not using the QT default setting as this is intended to be changed in the ini
886 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
882 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); 887 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
883 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); 888 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
884 WriteSetting(QStringLiteral("program_args"), 889 WriteSetting(QStringLiteral("program_args"),
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 7613197f2..f2977719c 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -182,6 +182,8 @@ void ConfigureInput::UpdateUIEnabled() {
182 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0); 182 players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
183 } 183 }
184 184
185 ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() &&
186 !ui->use_docked_mode->isChecked());
185 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked()); 187 ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
186 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() && 188 ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
187 !ui->use_docked_mode->isChecked()); 189 !ui->use_docked_mode->isChecked());
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 067d58d80..5cadfd191 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -374,6 +374,8 @@ void Config::ReadValues() {
374 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false); 374 Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
375 375
376 // Debugging 376 // Debugging
377 Settings::values.record_frame_times =
378 sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
377 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false); 379 Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
378 Settings::values.gdbstub_port = 380 Settings::values.gdbstub_port =
379 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689)); 381 static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index 0cfc111a6..f9f244522 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -213,6 +213,8 @@ region_value =
213log_filter = *:Trace 213log_filter = *:Trace
214 214
215[Debugging] 215[Debugging]
216# Record frame time data, can be found in the log directory. Boolean value
217record_frame_times =
216# Port for listening to GDB connections. 218# Port for listening to GDB connections.
217use_gdbstub=false 219use_gdbstub=false
218gdbstub_port=24689 220gdbstub_port=24689