summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-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/engines/shader_bytecode.h13
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h4
-rw-r--r--src/video_core/shader/decode/arithmetic_integer.cpp29
-rw-r--r--src/yuzu/configuration/config.cpp5
-rw-r--r--src/yuzu/configuration/configure_input.cpp2
-rw-r--r--src/yuzu/main.cpp8
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/default_ini.h2
16 files changed, 197 insertions, 30 deletions
diff --git a/README.md b/README.md
index 430c6dd65..ecace43f2 100644
--- a/README.md
+++ b/README.md
@@ -6,9 +6,11 @@ yuzu emulator
6 6
7yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/). 7yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
8 8
9It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes. 9It is written in C++ with portability in mind, with builds actively maintained for Windows and Linux. The emulator is capable of running several commercial games.
10 10
11yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. yuzu can boot some games, to varying degrees of success. 11yuzu only emulates a subset of Switch hardware and therefore most commercial games **do not** run at full speed or are not fully functional.
12
13Do you want to check which games are compatible and which ones are not? Please visit our [Compatibility page](https://yuzu-emu.org/game/)!
12 14
13yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included. 15yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
14 16
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/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index a6110bd86..28272ef6f 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -950,6 +950,11 @@ union Instruction {
950 } isetp; 950 } isetp;
951 951
952 union { 952 union {
953 BitField<48, 1, u64> is_signed;
954 BitField<49, 3, PredCondition> cond;
955 } icmp;
956
957 union {
953 BitField<0, 3, u64> pred0; 958 BitField<0, 3, u64> pred0;
954 BitField<3, 3, u64> pred3; 959 BitField<3, 3, u64> pred3;
955 BitField<12, 3, u64> pred12; 960 BitField<12, 3, u64> pred12;
@@ -1645,6 +1650,10 @@ public:
1645 SEL_C, 1650 SEL_C,
1646 SEL_R, 1651 SEL_R,
1647 SEL_IMM, 1652 SEL_IMM,
1653 ICMP_RC,
1654 ICMP_R,
1655 ICMP_CR,
1656 ICMP_IMM,
1648 MUFU, // Multi-Function Operator 1657 MUFU, // Multi-Function Operator
1649 RRO_C, // Range Reduction Operator 1658 RRO_C, // Range Reduction Operator
1650 RRO_R, 1659 RRO_R,
@@ -1910,6 +1919,10 @@ private:
1910 INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"), 1919 INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"),
1911 INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"), 1920 INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"),
1912 INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"), 1921 INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"),
1922 INST("010100110100----", Id::ICMP_RC, Type::ArithmeticInteger, "ICMP_RC"),
1923 INST("010110110100----", Id::ICMP_R, Type::ArithmeticInteger, "ICMP_R"),
1924 INST("010010110100----", Id::ICMP_CR, Type::ArithmeticInteger, "ICMP_CR"),
1925 INST("0011011-0100----", Id::ICMP_IMM, Type::ArithmeticInteger, "ICMP_IMM"),
1913 INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"), 1926 INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"),
1914 INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"), 1927 INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"),
1915 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"), 1928 INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
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/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp
index c8c1a7f40..b73f6536e 100644
--- a/src/video_core/shader/decode/arithmetic_integer.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer.cpp
@@ -138,6 +138,35 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
138 SetRegister(bb, instr.gpr0, value); 138 SetRegister(bb, instr.gpr0, value);
139 break; 139 break;
140 } 140 }
141 case OpCode::Id::ICMP_CR:
142 case OpCode::Id::ICMP_R:
143 case OpCode::Id::ICMP_RC:
144 case OpCode::Id::ICMP_IMM: {
145 const Node zero = Immediate(0);
146
147 const auto [op_b, test] = [&]() -> std::pair<Node, Node> {
148 switch (opcode->get().GetId()) {
149 case OpCode::Id::ICMP_CR:
150 return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset),
151 GetRegister(instr.gpr39)};
152 case OpCode::Id::ICMP_R:
153 return {GetRegister(instr.gpr20), GetRegister(instr.gpr39)};
154 case OpCode::Id::ICMP_RC:
155 return {GetRegister(instr.gpr39),
156 GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)};
157 case OpCode::Id::ICMP_IMM:
158 return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
159 default:
160 UNREACHABLE();
161 return {zero, zero};
162 }
163 }();
164 const Node op_a = GetRegister(instr.gpr8);
165 const Node comparison =
166 GetPredicateComparisonInteger(instr.icmp.cond, instr.icmp.is_signed != 0, test, zero);
167 SetRegister(bb, instr.gpr0, Operation(OperationCode::Select, comparison, op_a, op_b));
168 break;
169 }
141 case OpCode::Id::LOP_C: 170 case OpCode::Id::LOP_C:
142 case OpCode::Id::LOP_R: 171 case OpCode::Id::LOP_R:
143 case OpCode::Id::LOP_IMM: { 172 case OpCode::Id::LOP_IMM: {
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/main.cpp b/src/yuzu/main.cpp
index 9d2072e14..0865385e3 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -54,6 +54,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
54#include <QProgressDialog> 54#include <QProgressDialog>
55#include <QShortcut> 55#include <QShortcut>
56#include <QStatusBar> 56#include <QStatusBar>
57#include <QSysInfo>
57#include <QtConcurrent/QtConcurrent> 58#include <QtConcurrent/QtConcurrent>
58 59
59#include <fmt/format.h> 60#include <fmt/format.h>
@@ -66,6 +67,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
66#include "common/microprofile.h" 67#include "common/microprofile.h"
67#include "common/scm_rev.h" 68#include "common/scm_rev.h"
68#include "common/scope_exit.h" 69#include "common/scope_exit.h"
70#ifdef ARCHITECTURE_x86_64
71#include "common/x64/cpu_detect.h"
72#endif
69#include "common/telemetry.h" 73#include "common/telemetry.h"
70#include "core/core.h" 74#include "core/core.h"
71#include "core/crypto/key_manager.h" 75#include "core/crypto/key_manager.h"
@@ -205,6 +209,10 @@ GMainWindow::GMainWindow()
205 209
206 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, 210 LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
207 Common::g_scm_desc); 211 Common::g_scm_desc);
212#ifdef ARCHITECTURE_x86_64
213 LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string);
214#endif
215 LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
208 UpdateWindowTitle(); 216 UpdateWindowTitle();
209 217
210 show(); 218 show();
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