summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/logging/backend.cpp351
-rw-r--r--src/common/logging/backend.h113
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/types.h1
-rw-r--r--src/common/settings.h5
-rw-r--r--src/common/x64/xbyak_abi.h2
-rw-r--r--src/common/x64/xbyak_util.h2
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/core.cpp17
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/hle/kernel/kernel.cpp23
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp31
-rw-r--r--src/core/hle/service/ngct/ngct.cpp46
-rw-r--r--src/core/hle/service/ngct/ngct.h20
-rw-r--r--src/core/hle/service/nifm/nifm.cpp132
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/vi/vi.cpp2
-rw-r--r--src/core/network/network.cpp64
-rw-r--r--src/core/network/network.h24
-rw-r--r--src/core/network/network_interface.cpp210
-rw-r--r--src/core/network/network_interface.h29
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp8
-rw-r--r--src/tests/common/param_package.cpp2
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp1
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h85
-rw-r--r--src/video_core/macro/macro_jit_x64.h2
-rw-r--r--src/video_core/memory_manager.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp2
-rw-r--r--src/video_core/textures/decoders.cpp173
-rw-r--r--src/video_core/textures/texture.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp16
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h10
-rw-r--r--src/yuzu/CMakeLists.txt15
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp6
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure.ui10
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp34
-rw-r--r--src/yuzu/configuration/configure_network.cpp (renamed from src/yuzu/configuration/configure_service.cpp)39
-rw-r--r--src/yuzu/configuration/configure_network.h (renamed from src/yuzu/configuration/configure_service.h)10
-rw-r--r--src/yuzu/configuration/configure_network.ui (renamed from src/yuzu/configuration/configure_service.ui)63
-rw-r--r--src/yuzu/debugger/console.cpp11
-rw-r--r--src/yuzu/main.cpp19
-rw-r--r--src/yuzu_cmd/yuzu.cpp22
46 files changed, 1021 insertions, 607 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 61dddab3f..949384fd3 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -2,13 +2,10 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <atomic> 5#include <atomic>
7#include <chrono> 6#include <chrono>
8#include <climits> 7#include <climits>
9#include <condition_variable> 8#include <exception>
10#include <memory>
11#include <mutex>
12#include <thread> 9#include <thread>
13#include <vector> 10#include <vector>
14 11
@@ -16,104 +13,229 @@
16#include <windows.h> // For OutputDebugStringW 13#include <windows.h> // For OutputDebugStringW
17#endif 14#endif
18 15
19#include "common/assert.h"
20#include "common/fs/file.h" 16#include "common/fs/file.h"
21#include "common/fs/fs.h" 17#include "common/fs/fs.h"
18#include "common/fs/fs_paths.h"
19#include "common/fs/path_util.h"
22#include "common/literals.h" 20#include "common/literals.h"
23 21
24#include "common/logging/backend.h" 22#include "common/logging/backend.h"
25#include "common/logging/log.h" 23#include "common/logging/log.h"
26#include "common/logging/text_formatter.h" 24#include "common/logging/text_formatter.h"
27#include "common/settings.h" 25#include "common/settings.h"
26#ifdef _WIN32
28#include "common/string_util.h" 27#include "common/string_util.h"
28#endif
29#include "common/threadsafe_queue.h" 29#include "common/threadsafe_queue.h"
30 30
31namespace Common::Log { 31namespace Common::Log {
32 32
33namespace {
34
33/** 35/**
34 * Static state as a singleton. 36 * Interface for logging backends.
35 */ 37 */
36class Impl { 38class Backend {
37public: 39public:
38 static Impl& Instance() { 40 virtual ~Backend() = default;
39 static Impl backend; 41
40 return backend; 42 virtual void Write(const Entry& entry) = 0;
43
44 virtual void EnableForStacktrace() = 0;
45
46 virtual void Flush() = 0;
47};
48
49/**
50 * Backend that writes to stderr and with color
51 */
52class ColorConsoleBackend final : public Backend {
53public:
54 explicit ColorConsoleBackend() = default;
55
56 ~ColorConsoleBackend() override = default;
57
58 void Write(const Entry& entry) override {
59 if (enabled.load(std::memory_order_relaxed)) {
60 PrintColoredMessage(entry);
61 }
41 } 62 }
42 63
43 Impl(const Impl&) = delete; 64 void Flush() override {
44 Impl& operator=(const Impl&) = delete; 65 // stderr shouldn't be buffered
66 }
45 67
46 Impl(Impl&&) = delete; 68 void EnableForStacktrace() override {
47 Impl& operator=(Impl&&) = delete; 69 enabled = true;
70 }
48 71
49 void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, 72 void SetEnabled(bool enabled_) {
50 const char* function, std::string message) { 73 enabled = enabled_;
51 message_queue.Push( 74 }
52 CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); 75
76private:
77 std::atomic_bool enabled{false};
78};
79
80/**
81 * Backend that writes to a file passed into the constructor
82 */
83class FileBackend final : public Backend {
84public:
85 explicit FileBackend(const std::filesystem::path& filename) {
86 auto old_filename = filename;
87 old_filename += ".old.txt";
88
89 // Existence checks are done within the functions themselves.
90 // We don't particularly care if these succeed or not.
91 static_cast<void>(FS::RemoveFile(old_filename));
92 static_cast<void>(FS::RenameFile(filename, old_filename));
93
94 file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write,
95 FS::FileType::TextFile);
96 }
97
98 ~FileBackend() override = default;
99
100 void Write(const Entry& entry) override {
101 if (!enabled) {
102 return;
103 }
104
105 bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
106
107 using namespace Common::Literals;
108 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
109 const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB;
110 const bool write_limit_exceeded = bytes_written > write_limit;
111 if (entry.log_level >= Level::Error || write_limit_exceeded) {
112 if (write_limit_exceeded) {
113 // Stop writing after the write limit is exceeded.
114 // Don't close the file so we can print a stacktrace if necessary
115 enabled = false;
116 }
117 file->Flush();
118 }
119 }
120
121 void Flush() override {
122 file->Flush();
123 }
124
125 void EnableForStacktrace() override {
126 enabled = true;
127 bytes_written = 0;
53 } 128 }
54 129
55 void AddBackend(std::unique_ptr<Backend> backend) { 130private:
56 std::lock_guard lock{writing_mutex}; 131 std::unique_ptr<FS::IOFile> file;
57 backends.push_back(std::move(backend)); 132 bool enabled = true;
133 std::size_t bytes_written = 0;
134};
135
136/**
137 * Backend that writes to Visual Studio's output window
138 */
139class DebuggerBackend final : public Backend {
140public:
141 explicit DebuggerBackend() = default;
142
143 ~DebuggerBackend() override = default;
144
145 void Write(const Entry& entry) override {
146#ifdef _WIN32
147 ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
148#endif
58 } 149 }
59 150
60 void RemoveBackend(std::string_view backend_name) { 151 void Flush() override {}
61 std::lock_guard lock{writing_mutex}; 152
153 void EnableForStacktrace() override {}
154};
155
156bool initialization_in_progress_suppress_logging = true;
62 157
63 std::erase_if(backends, [&backend_name](const auto& backend) { 158/**
64 return backend_name == backend->GetName(); 159 * Static state as a singleton.
65 }); 160 */
161class Impl {
162public:
163 static Impl& Instance() {
164 if (!instance) {
165 throw std::runtime_error("Using Logging instance before its initialization");
166 }
167 return *instance;
66 } 168 }
67 169
68 const Filter& GetGlobalFilter() const { 170 static void Initialize() {
69 return filter; 171 if (instance) {
172 LOG_WARNING(Log, "Reinitializing logging backend");
173 return;
174 }
175 using namespace Common::FS;
176 const auto& log_dir = GetYuzuPath(YuzuPath::LogDir);
177 void(CreateDir(log_dir));
178 Filter filter;
179 filter.ParseFilterString(Settings::values.log_filter.GetValue());
180 instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter),
181 Deleter);
182 initialization_in_progress_suppress_logging = false;
70 } 183 }
71 184
185 Impl(const Impl&) = delete;
186 Impl& operator=(const Impl&) = delete;
187
188 Impl(Impl&&) = delete;
189 Impl& operator=(Impl&&) = delete;
190
72 void SetGlobalFilter(const Filter& f) { 191 void SetGlobalFilter(const Filter& f) {
73 filter = f; 192 filter = f;
74 } 193 }
75 194
76 Backend* GetBackend(std::string_view backend_name) { 195 void SetColorConsoleBackendEnabled(bool enabled) {
77 const auto it = 196 color_console_backend.SetEnabled(enabled);
78 std::find_if(backends.begin(), backends.end(), 197 }
79 [&backend_name](const auto& i) { return backend_name == i->GetName(); }); 198
80 if (it == backends.end()) 199 void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
81 return nullptr; 200 const char* function, std::string message) {
82 return it->get(); 201 if (!filter.CheckMessage(log_class, log_level))
202 return;
203 const Entry& entry =
204 CreateEntry(log_class, log_level, filename, line_num, function, std::move(message));
205 message_queue.Push(entry);
83 } 206 }
84 207
85private: 208private:
86 Impl() { 209 Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
87 backend_thread = std::thread([&] { 210 : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] {
88 Entry entry; 211 Common::SetCurrentThreadName("yuzu:Log");
89 auto write_logs = [&](Entry& e) { 212 Entry entry;
90 std::lock_guard lock{writing_mutex}; 213 const auto write_logs = [this, &entry]() {
91 for (const auto& backend : backends) { 214 ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
92 backend->Write(e); 215 };
93 } 216 while (true) {
94 }; 217 entry = message_queue.PopWait();
95 while (true) { 218 if (entry.final_entry) {
96 entry = message_queue.PopWait(); 219 break;
97 if (entry.final_entry) { 220 }
98 break; 221 write_logs();
99 } 222 }
100 write_logs(entry); 223 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
101 } 224 // case where a system is repeatedly spamming logs even on close.
225 int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
226 while (max_logs_to_write-- && message_queue.Pop(entry)) {
227 write_logs();
228 }
229 })} {}
102 230
103 // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a 231 ~Impl() {
104 // case where a system is repeatedly spamming logs even on close. 232 StopBackendThread();
105 const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
106 int logs_written = 0;
107 while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
108 write_logs(entry);
109 }
110 });
111 } 233 }
112 234
113 ~Impl() { 235 void StopBackendThread() {
114 Entry entry; 236 Entry stop_entry{};
115 entry.final_entry = true; 237 stop_entry.final_entry = true;
116 message_queue.Push(entry); 238 message_queue.Push(stop_entry);
117 backend_thread.join(); 239 backend_thread.join();
118 } 240 }
119 241
@@ -135,100 +257,51 @@ private:
135 }; 257 };
136 } 258 }
137 259
138 std::mutex writing_mutex; 260 void ForEachBackend(auto lambda) {
139 std::thread backend_thread; 261 lambda(static_cast<Backend&>(debugger_backend));
140 std::vector<std::unique_ptr<Backend>> backends; 262 lambda(static_cast<Backend&>(color_console_backend));
141 MPSCQueue<Entry> message_queue; 263 lambda(static_cast<Backend&>(file_backend));
142 Filter filter; 264 }
143 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
144};
145
146ConsoleBackend::~ConsoleBackend() = default;
147
148void ConsoleBackend::Write(const Entry& entry) {
149 PrintMessage(entry);
150}
151
152ColorConsoleBackend::~ColorConsoleBackend() = default;
153
154void ColorConsoleBackend::Write(const Entry& entry) {
155 PrintColoredMessage(entry);
156}
157
158FileBackend::FileBackend(const std::filesystem::path& filename) {
159 auto old_filename = filename;
160 old_filename += ".old.txt";
161
162 // Existence checks are done within the functions themselves.
163 // We don't particularly care if these succeed or not.
164 FS::RemoveFile(old_filename);
165 void(FS::RenameFile(filename, old_filename));
166
167 file =
168 std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
169}
170
171FileBackend::~FileBackend() = default;
172 265
173void FileBackend::Write(const Entry& entry) { 266 static void Deleter(Impl* ptr) {
174 if (!file->IsOpen()) { 267 delete ptr;
175 return;
176 } 268 }
177 269
178 using namespace Common::Literals; 270 static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
179 // Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
180 constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
181 constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
182 271
183 const bool write_limit_exceeded = 272 Filter filter;
184 bytes_written > MAX_BYTES_WRITTEN_EXTENDED || 273 DebuggerBackend debugger_backend{};
185 (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging); 274 ColorConsoleBackend color_console_backend{};
275 FileBackend file_backend;
186 276
187 // Close the file after the write limit is exceeded. 277 std::thread backend_thread;
188 if (write_limit_exceeded) { 278 MPSCQueue<Entry> message_queue{};
189 file->Close(); 279 std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
190 return; 280};
191 } 281} // namespace
192 282
193 bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); 283void Initialize() {
194 if (entry.log_level >= Level::Error) { 284 Impl::Initialize();
195 file->Flush();
196 }
197} 285}
198 286
199DebuggerBackend::~DebuggerBackend() = default; 287void DisableLoggingInTests() {
200 288 initialization_in_progress_suppress_logging = true;
201void DebuggerBackend::Write(const Entry& entry) {
202#ifdef _WIN32
203 ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
204#endif
205} 289}
206 290
207void SetGlobalFilter(const Filter& filter) { 291void SetGlobalFilter(const Filter& filter) {
208 Impl::Instance().SetGlobalFilter(filter); 292 Impl::Instance().SetGlobalFilter(filter);
209} 293}
210 294
211void AddBackend(std::unique_ptr<Backend> backend) { 295void SetColorConsoleBackendEnabled(bool enabled) {
212 Impl::Instance().AddBackend(std::move(backend)); 296 Impl::Instance().SetColorConsoleBackendEnabled(enabled);
213}
214
215void RemoveBackend(std::string_view backend_name) {
216 Impl::Instance().RemoveBackend(backend_name);
217}
218
219Backend* GetBackend(std::string_view backend_name) {
220 return Impl::Instance().GetBackend(backend_name);
221} 297}
222 298
223void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, 299void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
224 unsigned int line_num, const char* function, const char* format, 300 unsigned int line_num, const char* function, const char* format,
225 const fmt::format_args& args) { 301 const fmt::format_args& args) {
226 auto& instance = Impl::Instance(); 302 if (!initialization_in_progress_suppress_logging) {
227 const auto& filter = instance.GetGlobalFilter(); 303 Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
228 if (!filter.CheckMessage(log_class, log_level)) 304 fmt::vformat(format, args));
229 return; 305 }
230
231 instance.PushEntry(log_class, log_level, filename, line_num, function,
232 fmt::vformat(format, args));
233} 306}
234} // namespace Common::Log 307} // namespace Common::Log
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 4b9a910c1..cb7839ee9 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -5,120 +5,21 @@
5#pragma once 5#pragma once
6 6
7#include <filesystem> 7#include <filesystem>
8#include <memory>
9#include <string>
10#include <string_view>
11#include "common/logging/filter.h" 8#include "common/logging/filter.h"
12#include "common/logging/log.h"
13
14namespace Common::FS {
15class IOFile;
16}
17 9
18namespace Common::Log { 10namespace Common::Log {
19 11
20class Filter; 12class Filter;
21 13
22/** 14/// Initializes the logging system. This should be the first thing called in main.
23 * Interface for logging backends. As loggers can be created and removed at runtime, this can be 15void Initialize();
24 * used by a frontend for adding a custom logging backend as needed
25 */
26class Backend {
27public:
28 virtual ~Backend() = default;
29
30 virtual void SetFilter(const Filter& new_filter) {
31 filter = new_filter;
32 }
33 virtual const char* GetName() const = 0;
34 virtual void Write(const Entry& entry) = 0;
35
36private:
37 Filter filter;
38};
39
40/**
41 * Backend that writes to stderr without any color commands
42 */
43class ConsoleBackend : public Backend {
44public:
45 ~ConsoleBackend() override;
46
47 static const char* Name() {
48 return "console";
49 }
50 const char* GetName() const override {
51 return Name();
52 }
53 void Write(const Entry& entry) override;
54};
55
56/**
57 * Backend that writes to stderr and with color
58 */
59class ColorConsoleBackend : public Backend {
60public:
61 ~ColorConsoleBackend() override;
62
63 static const char* Name() {
64 return "color_console";
65 }
66
67 const char* GetName() const override {
68 return Name();
69 }
70 void Write(const Entry& entry) override;
71};
72 16
73/** 17void DisableLoggingInTests();
74 * Backend that writes to a file passed into the constructor
75 */
76class FileBackend : public Backend {
77public:
78 explicit FileBackend(const std::filesystem::path& filename);
79 ~FileBackend() override;
80
81 static const char* Name() {
82 return "file";
83 }
84
85 const char* GetName() const override {
86 return Name();
87 }
88
89 void Write(const Entry& entry) override;
90
91private:
92 std::unique_ptr<FS::IOFile> file;
93 std::size_t bytes_written = 0;
94};
95
96/**
97 * Backend that writes to Visual Studio's output window
98 */
99class DebuggerBackend : public Backend {
100public:
101 ~DebuggerBackend() override;
102
103 static const char* Name() {
104 return "debugger";
105 }
106 const char* GetName() const override {
107 return Name();
108 }
109 void Write(const Entry& entry) override;
110};
111
112void AddBackend(std::unique_ptr<Backend> backend);
113
114void RemoveBackend(std::string_view backend_name);
115
116Backend* GetBackend(std::string_view backend_name);
117 18
118/** 19/**
119 * The global filter will prevent any messages from even being processed if they are filtered. Each 20 * The global filter will prevent any messages from even being processed if they are filtered.
120 * backend can have a filter, but if the level is lower than the global filter, the backend will
121 * never get the message
122 */ 21 */
123void SetGlobalFilter(const Filter& filter); 22void SetGlobalFilter(const Filter& filter);
124} // namespace Common::Log \ No newline at end of file 23
24void SetColorConsoleBackendEnabled(bool enabled);
25} // namespace Common::Log
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index f055f0e11..42744c994 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -111,6 +111,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
111 SUB(Service, NCM) \ 111 SUB(Service, NCM) \
112 SUB(Service, NFC) \ 112 SUB(Service, NFC) \
113 SUB(Service, NFP) \ 113 SUB(Service, NFP) \
114 SUB(Service, NGCT) \
114 SUB(Service, NIFM) \ 115 SUB(Service, NIFM) \
115 SUB(Service, NIM) \ 116 SUB(Service, NIM) \
116 SUB(Service, NPNS) \ 117 SUB(Service, NPNS) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 7ad0334fc..ddf9d27ca 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -81,6 +81,7 @@ enum class Class : u8 {
81 Service_NCM, ///< The NCM service 81 Service_NCM, ///< The NCM service
82 Service_NFC, ///< The NFC (Near-field communication) service 82 Service_NFC, ///< The NFC (Near-field communication) service
83 Service_NFP, ///< The NFP service 83 Service_NFP, ///< The NFP service
84 Service_NGCT, ///< The NGCT (No Good Content for Terra) service
84 Service_NIFM, ///< The NIFM (Network interface) service 85 Service_NIFM, ///< The NIFM (Network interface) service
85 Service_NIM, ///< The NIM service 86 Service_NIM, ///< The NIM service
86 Service_NPNS, ///< The NPNS service 87 Service_NPNS, ///< The NPNS service
diff --git a/src/common/settings.h b/src/common/settings.h
index 1ba9b606c..20769d310 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -489,7 +489,7 @@ struct Values {
489 std::chrono::seconds custom_rtc_differential; 489 std::chrono::seconds custom_rtc_differential;
490 490
491 BasicSetting<s32> current_user{0, "current_user"}; 491 BasicSetting<s32> current_user{0, "current_user"};
492 RangedSetting<s32> language_index{1, 0, 16, "language_index"}; 492 RangedSetting<s32> language_index{1, 0, 17, "language_index"};
493 RangedSetting<s32> region_index{1, 0, 6, "region_index"}; 493 RangedSetting<s32> region_index{1, 0, 6, "region_index"};
494 RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"}; 494 RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"};
495 RangedSetting<s32> sound_index{1, 0, 2, "sound_index"}; 495 RangedSetting<s32> sound_index{1, 0, 2, "sound_index"};
@@ -558,9 +558,10 @@ struct Values {
558 BasicSetting<std::string> log_filter{"*:Info", "log_filter"}; 558 BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
559 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"}; 559 BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
560 560
561 // Services 561 // Network
562 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"}; 562 BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
563 BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"}; 563 BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
564 BasicSetting<std::string> network_interface{std::string(), "network_interface"};
564 565
565 // WebService 566 // WebService
566 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"}; 567 BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index c2c9b6134..0ddf9b83e 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -6,7 +6,7 @@
6 6
7#include <bitset> 7#include <bitset>
8#include <initializer_list> 8#include <initializer_list>
9#include <xbyak.h> 9#include <xbyak/xbyak.h>
10#include "common/assert.h" 10#include "common/assert.h"
11 11
12namespace Common::X64 { 12namespace Common::X64 {
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
index df17f8cbe..44d2558f1 100644
--- a/src/common/x64/xbyak_util.h
+++ b/src/common/x64/xbyak_util.h
@@ -5,7 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <type_traits> 7#include <type_traits>
8#include <xbyak.h> 8#include <xbyak/xbyak.h>
9#include "common/x64/xbyak_abi.h" 9#include "common/x64/xbyak_abi.h"
10 10
11namespace Common::X64 { 11namespace Common::X64 {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5c99c00f5..87d47e2e5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -452,6 +452,8 @@ add_library(core STATIC
452 hle/service/nfp/nfp.h 452 hle/service/nfp/nfp.h
453 hle/service/nfp/nfp_user.cpp 453 hle/service/nfp/nfp_user.cpp
454 hle/service/nfp/nfp_user.h 454 hle/service/nfp/nfp_user.h
455 hle/service/ngct/ngct.cpp
456 hle/service/ngct/ngct.h
455 hle/service/nifm/nifm.cpp 457 hle/service/nifm/nifm.cpp
456 hle/service/nifm/nifm.h 458 hle/service/nifm/nifm.h
457 hle/service/nim/nim.cpp 459 hle/service/nim/nim.cpp
@@ -636,6 +638,8 @@ add_library(core STATIC
636 memory.h 638 memory.h
637 network/network.cpp 639 network/network.cpp
638 network/network.h 640 network/network.h
641 network/network_interface.cpp
642 network/network_interface.h
639 network/sockets.h 643 network/sockets.h
640 perf_stats.cpp 644 perf_stats.cpp
641 perf_stats.h 645 perf_stats.h
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d3e84c4ef..ba4629993 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -4,6 +4,7 @@
4 4
5#include <array> 5#include <array>
6#include <atomic> 6#include <atomic>
7#include <exception>
7#include <memory> 8#include <memory>
8#include <utility> 9#include <utility>
9 10
@@ -84,8 +85,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
84 85
85} // Anonymous namespace 86} // Anonymous namespace
86 87
87/*static*/ System System::s_instance;
88
89FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, 88FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
90 const std::string& path) { 89 const std::string& path) {
91 // To account for split 00+01+etc files. 90 // To account for split 00+01+etc files.
@@ -425,6 +424,20 @@ struct System::Impl {
425System::System() : impl{std::make_unique<Impl>(*this)} {} 424System::System() : impl{std::make_unique<Impl>(*this)} {}
426System::~System() = default; 425System::~System() = default;
427 426
427System& System::GetInstance() {
428 if (!s_instance) {
429 throw std::runtime_error("Using System instance before its initialization");
430 }
431 return *s_instance;
432}
433
434void System::InitializeGlobalInstance() {
435 if (s_instance) {
436 throw std::runtime_error("Reinitializing Global System instance.");
437 }
438 s_instance = std::unique_ptr<System>(new System);
439}
440
428CpuManager& System::GetCpuManager() { 441CpuManager& System::GetCpuManager() {
429 return impl->cpu_manager; 442 return impl->cpu_manager;
430} 443}
diff --git a/src/core/core.h b/src/core/core.h
index ea143043c..715ab88e7 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -120,9 +120,9 @@ public:
120 * Gets the instance of the System singleton class. 120 * Gets the instance of the System singleton class.
121 * @returns Reference to the instance of the System singleton class. 121 * @returns Reference to the instance of the System singleton class.
122 */ 122 */
123 [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() { 123 [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance();
124 return s_instance; 124
125 } 125 static void InitializeGlobalInstance();
126 126
127 /// Enumeration representing the return values of the System Initialize and Load process. 127 /// Enumeration representing the return values of the System Initialize and Load process.
128 enum class ResultStatus : u32 { 128 enum class ResultStatus : u32 {
@@ -396,7 +396,7 @@ private:
396 struct Impl; 396 struct Impl;
397 std::unique_ptr<Impl> impl; 397 std::unique_ptr<Impl> impl;
398 398
399 static System s_instance; 399 inline static std::unique_ptr<System> s_instance{};
400}; 400};
401 401
402} // namespace Core 402} // namespace Core
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 92fbc5532..bea945301 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -267,20 +267,23 @@ struct KernelCore::Impl {
267 } 267 }
268 } 268 }
269 269
270 /// Creates a new host thread ID, should only be called by GetHostThreadId 270 static inline thread_local u32 host_thread_id = UINT32_MAX;
271 u32 AllocateHostThreadId(std::optional<std::size_t> core_id) { 271
272 if (core_id) { 272 /// Gets the host thread ID for the caller, allocating a new one if this is the first time
273 // The first for slots are reserved for CPU core threads 273 u32 GetHostThreadId(std::size_t core_id) {
274 ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES); 274 if (host_thread_id == UINT32_MAX) {
275 return static_cast<u32>(*core_id); 275 // The first four slots are reserved for CPU core threads
276 } else { 276 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
277 return next_host_thread_id++; 277 host_thread_id = static_cast<u32>(core_id);
278 } 278 }
279 return host_thread_id;
279 } 280 }
280 281
281 /// Gets the host thread ID for the caller, allocating a new one if this is the first time 282 /// Gets the host thread ID for the caller, allocating a new one if this is the first time
282 u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) { 283 u32 GetHostThreadId() {
283 const thread_local auto host_thread_id{AllocateHostThreadId(core_id)}; 284 if (host_thread_id == UINT32_MAX) {
285 host_thread_id = next_host_thread_id++;
286 }
284 return host_thread_id; 287 return host_thread_id;
285 } 288 }
286 289
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 2eb532472..62fb06c45 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1078,8 +1078,8 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
1078 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { 1078 for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
1079 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) { 1079 if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) {
1080 current = true; 1080 current = true;
1081 break;
1081 } 1082 }
1082 break;
1083 } 1083 }
1084 1084
1085 // If the thread is current, retry until it isn't. 1085 // If the thread is current, retry until it isn't.
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index ef6854d62..36a4aa9cd 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -16,6 +16,30 @@
16 16
17namespace Service::AM::Applets { 17namespace Service::AM::Applets {
18 18
19struct ErrorCode {
20 u32 error_category{};
21 u32 error_number{};
22
23 static constexpr ErrorCode FromU64(u64 error_code) {
24 return {
25 .error_category{static_cast<u32>(error_code >> 32)},
26 .error_number{static_cast<u32>(error_code & 0xFFFFFFFF)},
27 };
28 }
29
30 static constexpr ErrorCode FromResultCode(ResultCode result) {
31 return {
32 .error_category{2000 + static_cast<u32>(result.module.Value())},
33 .error_number{result.description.Value()},
34 };
35 }
36
37 constexpr ResultCode ToResultCode() const {
38 return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number};
39 }
40};
41static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size.");
42
19#pragma pack(push, 4) 43#pragma pack(push, 4)
20struct ShowError { 44struct ShowError {
21 u8 mode; 45 u8 mode;
@@ -76,12 +100,7 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) {
76} 100}
77 101
78ResultCode Decode64BitError(u64 error) { 102ResultCode Decode64BitError(u64 error) {
79 const auto description = (error >> 32) & 0x1FFF; 103 return ErrorCode::FromU64(error).ToResultCode();
80 auto module = error & 0x3FF;
81 if (module >= 2000)
82 module -= 2000;
83 module &= 0x1FF;
84 return {static_cast<ErrorModule>(module), static_cast<u32>(description)};
85} 104}
86 105
87} // Anonymous namespace 106} // Anonymous namespace
diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp
new file mode 100644
index 000000000..deb3abb28
--- /dev/null
+++ b/src/core/hle/service/ngct/ngct.cpp
@@ -0,0 +1,46 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#include "common/string_util.h"
6#include "core/core.h"
7#include "core/hle/ipc_helpers.h"
8#include "core/hle/service/ngct/ngct.h"
9#include "core/hle/service/service.h"
10
11namespace Service::NGCT {
12
13class IService final : public ServiceFramework<IService> {
14public:
15 explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
16 // clang-format off
17 static const FunctionInfo functions[] = {
18 {0, nullptr, "Match"},
19 {1, &IService::Filter, "Filter"},
20 };
21 // clang-format on
22
23 RegisterHandlers(functions);
24 }
25
26private:
27 void Filter(Kernel::HLERequestContext& ctx) {
28 const auto buffer = ctx.ReadBuffer();
29 const auto text = Common::StringFromFixedZeroTerminatedBuffer(
30 reinterpret_cast<const char*>(buffer.data()), buffer.size());
31
32 LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text);
33
34 // Return the same string since we don't censor anything
35 ctx.WriteBuffer(buffer);
36
37 IPC::ResponseBuilder rb{ctx, 2};
38 rb.Push(ResultSuccess);
39 }
40};
41
42void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
43 std::make_shared<IService>(system)->InstallAsService(system.ServiceManager());
44}
45
46} // namespace Service::NGCT
diff --git a/src/core/hle/service/ngct/ngct.h b/src/core/hle/service/ngct/ngct.h
new file mode 100644
index 000000000..1f2a47b78
--- /dev/null
+++ b/src/core/hle/service/ngct/ngct.h
@@ -0,0 +1,20 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included
4
5#pragma once
6
7namespace Core {
8class System;
9}
10
11namespace Service::SM {
12class ServiceManager;
13}
14
15namespace Service::NGCT {
16
17/// Registers all NGCT services with the specified service manager.
18void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
19
20} // namespace Service::NGCT
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index e742db48f..9decb9290 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -11,6 +11,7 @@
11#include "core/hle/service/nifm/nifm.h" 11#include "core/hle/service/nifm/nifm.h"
12#include "core/hle/service/service.h" 12#include "core/hle/service/service.h"
13#include "core/network/network.h" 13#include "core/network/network.h"
14#include "core/network/network_interface.h"
14 15
15namespace Service::NIFM { 16namespace Service::NIFM {
16 17
@@ -179,10 +180,10 @@ private:
179 IPC::ResponseBuilder rb{ctx, 3}; 180 IPC::ResponseBuilder rb{ctx, 3};
180 rb.Push(ResultSuccess); 181 rb.Push(ResultSuccess);
181 182
182 if (Settings::values.bcat_backend.GetValue() == "none") { 183 if (Network::GetHostIPv4Address().has_value()) {
183 rb.PushEnum(RequestState::NotSubmitted);
184 } else {
185 rb.PushEnum(RequestState::Connected); 184 rb.PushEnum(RequestState::Connected);
185 } else {
186 rb.PushEnum(RequestState::NotSubmitted);
186 } 187 }
187 } 188 }
188 189
@@ -276,37 +277,45 @@ private:
276 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { 277 void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
277 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 278 LOG_WARNING(Service_NIFM, "(STUBBED) called");
278 279
279 const SfNetworkProfileData network_profile_data{ 280 const auto net_iface = Network::GetSelectedNetworkInterface();
280 .ip_setting_data{ 281
281 .ip_address_setting{ 282 const SfNetworkProfileData network_profile_data = [&net_iface] {
282 .is_automatic{true}, 283 if (!net_iface) {
283 .current_address{192, 168, 1, 100}, 284 return SfNetworkProfileData{};
284 .subnet_mask{255, 255, 255, 0}, 285 }
285 .gateway{192, 168, 1, 1}, 286
286 }, 287 return SfNetworkProfileData{
287 .dns_setting{ 288 .ip_setting_data{
288 .is_automatic{true}, 289 .ip_address_setting{
289 .primary_dns{1, 1, 1, 1}, 290 .is_automatic{true},
290 .secondary_dns{1, 0, 0, 1}, 291 .current_address{Network::TranslateIPv4(net_iface->ip_address)},
292 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
293 .gateway{Network::TranslateIPv4(net_iface->gateway)},
294 },
295 .dns_setting{
296 .is_automatic{true},
297 .primary_dns{1, 1, 1, 1},
298 .secondary_dns{1, 0, 0, 1},
299 },
300 .proxy_setting{
301 .enabled{false},
302 .port{},
303 .proxy_server{},
304 .automatic_auth_enabled{},
305 .user{},
306 .password{},
307 },
308 .mtu{1500},
291 }, 309 },
292 .proxy_setting{ 310 .uuid{0xdeadbeef, 0xdeadbeef},
293 .enabled{false}, 311 .network_name{"yuzu Network"},
294 .port{}, 312 .wireless_setting_data{
295 .proxy_server{}, 313 .ssid_length{12},
296 .automatic_auth_enabled{}, 314 .ssid{"yuzu Network"},
297 .user{}, 315 .passphrase{"yuzupassword"},
298 .password{},
299 }, 316 },
300 .mtu{1500}, 317 };
301 }, 318 }();
302 .uuid{0xdeadbeef, 0xdeadbeef},
303 .network_name{"yuzu Network"},
304 .wireless_setting_data{
305 .ssid_length{12},
306 .ssid{"yuzu Network"},
307 .passphrase{"yuzupassword"},
308 },
309 };
310 319
311 ctx.WriteBuffer(network_profile_data); 320 ctx.WriteBuffer(network_profile_data);
312 321
@@ -322,12 +331,15 @@ private:
322 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { 331 void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
323 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 332 LOG_WARNING(Service_NIFM, "(STUBBED) called");
324 333
325 const auto [ipv4, error] = Network::GetHostIPv4Address(); 334 auto ipv4 = Network::GetHostIPv4Address();
326 UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); 335 if (!ipv4) {
336 LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
337 ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
338 }
327 339
328 IPC::ResponseBuilder rb{ctx, 3}; 340 IPC::ResponseBuilder rb{ctx, 3};
329 rb.Push(ResultSuccess); 341 rb.Push(ResultSuccess);
330 rb.PushRaw(ipv4); 342 rb.PushRaw(*ipv4);
331 } 343 }
332 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { 344 void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
333 LOG_DEBUG(Service_NIFM, "called"); 345 LOG_DEBUG(Service_NIFM, "called");
@@ -348,25 +360,33 @@ private:
348 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 360 LOG_WARNING(Service_NIFM, "(STUBBED) called");
349 361
350 struct IpConfigInfo { 362 struct IpConfigInfo {
351 IpAddressSetting ip_address_setting; 363 IpAddressSetting ip_address_setting{};
352 DnsSetting dns_setting; 364 DnsSetting dns_setting{};
353 }; 365 };
354 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), 366 static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
355 "IpConfigInfo has incorrect size."); 367 "IpConfigInfo has incorrect size.");
356 368
357 const IpConfigInfo ip_config_info{ 369 const auto net_iface = Network::GetSelectedNetworkInterface();
358 .ip_address_setting{ 370
359 .is_automatic{true}, 371 const IpConfigInfo ip_config_info = [&net_iface] {
360 .current_address{192, 168, 1, 100}, 372 if (!net_iface) {
361 .subnet_mask{255, 255, 255, 0}, 373 return IpConfigInfo{};
362 .gateway{192, 168, 1, 1}, 374 }
363 }, 375
364 .dns_setting{ 376 return IpConfigInfo{
365 .is_automatic{true}, 377 .ip_address_setting{
366 .primary_dns{1, 1, 1, 1}, 378 .is_automatic{true},
367 .secondary_dns{1, 0, 0, 1}, 379 .current_address{Network::TranslateIPv4(net_iface->ip_address)},
368 }, 380 .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
369 }; 381 .gateway{Network::TranslateIPv4(net_iface->gateway)},
382 },
383 .dns_setting{
384 .is_automatic{true},
385 .primary_dns{1, 1, 1, 1},
386 .secondary_dns{1, 0, 0, 1},
387 },
388 };
389 }();
370 390
371 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; 391 IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
372 rb.Push(ResultSuccess); 392 rb.Push(ResultSuccess);
@@ -384,10 +404,10 @@ private:
384 404
385 IPC::ResponseBuilder rb{ctx, 3}; 405 IPC::ResponseBuilder rb{ctx, 3};
386 rb.Push(ResultSuccess); 406 rb.Push(ResultSuccess);
387 if (Settings::values.bcat_backend.GetValue() == "none") { 407 if (Network::GetHostIPv4Address().has_value()) {
388 rb.Push<u8>(0);
389 } else {
390 rb.Push<u8>(1); 408 rb.Push<u8>(1);
409 } else {
410 rb.Push<u8>(0);
391 } 411 }
392 } 412 }
393 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 413 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
@@ -395,10 +415,10 @@ private:
395 415
396 IPC::ResponseBuilder rb{ctx, 3}; 416 IPC::ResponseBuilder rb{ctx, 3};
397 rb.Push(ResultSuccess); 417 rb.Push(ResultSuccess);
398 if (Settings::values.bcat_backend.GetValue() == "none") { 418 if (Network::GetHostIPv4Address().has_value()) {
399 rb.Push<u8>(0);
400 } else {
401 rb.Push<u8>(1); 419 rb.Push<u8>(1);
420 } else {
421 rb.Push<u8>(0);
402 } 422 }
403 } 423 }
404}; 424};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index b3e50433b..065133166 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -46,6 +46,7 @@
46#include "core/hle/service/ncm/ncm.h" 46#include "core/hle/service/ncm/ncm.h"
47#include "core/hle/service/nfc/nfc.h" 47#include "core/hle/service/nfc/nfc.h"
48#include "core/hle/service/nfp/nfp.h" 48#include "core/hle/service/nfp/nfp.h"
49#include "core/hle/service/ngct/ngct.h"
49#include "core/hle/service/nifm/nifm.h" 50#include "core/hle/service/nifm/nifm.h"
50#include "core/hle/service/nim/nim.h" 51#include "core/hle/service/nim/nim.h"
51#include "core/hle/service/npns/npns.h" 52#include "core/hle/service/npns/npns.h"
@@ -271,6 +272,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
271 NCM::InstallInterfaces(*sm, system); 272 NCM::InstallInterfaces(*sm, system);
272 NFC::InstallInterfaces(*sm, system); 273 NFC::InstallInterfaces(*sm, system);
273 NFP::InstallInterfaces(*sm, system); 274 NFP::InstallInterfaces(*sm, system);
275 NGCT::InstallInterfaces(*sm, system);
274 NIFM::InstallInterfaces(*sm, system); 276 NIFM::InstallInterfaces(*sm, system);
275 NIM::InstallInterfaces(*sm, system); 277 NIM::InstallInterfaces(*sm, system);
276 NPNS::InstallInterfaces(*sm, system); 278 NPNS::InstallInterfaces(*sm, system);
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 3e5949d52..8e8fc40ca 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -1158,7 +1158,7 @@ private:
1158 1158
1159 const auto layer_id = nv_flinger.CreateLayer(display_id); 1159 const auto layer_id = nv_flinger.CreateLayer(display_id);
1160 if (!layer_id) { 1160 if (!layer_id) {
1161 LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id); 1161 LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
1162 IPC::ResponseBuilder rb{ctx, 2}; 1162 IPC::ResponseBuilder rb{ctx, 2};
1163 rb.Push(ERR_NOT_FOUND); 1163 rb.Push(ERR_NOT_FOUND);
1164 return; 1164 return;
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 375bc79ec..4732d4485 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -10,9 +10,10 @@
10#include "common/common_funcs.h" 10#include "common/common_funcs.h"
11 11
12#ifdef _WIN32 12#ifdef _WIN32
13#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
14#include <winsock2.h> 13#include <winsock2.h>
14#include <ws2tcpip.h>
15#elif YUZU_UNIX 15#elif YUZU_UNIX
16#include <arpa/inet.h>
16#include <errno.h> 17#include <errno.h>
17#include <fcntl.h> 18#include <fcntl.h>
18#include <netdb.h> 19#include <netdb.h>
@@ -27,7 +28,9 @@
27#include "common/assert.h" 28#include "common/assert.h"
28#include "common/common_types.h" 29#include "common/common_types.h"
29#include "common/logging/log.h" 30#include "common/logging/log.h"
31#include "common/settings.h"
30#include "core/network/network.h" 32#include "core/network/network.h"
33#include "core/network/network_interface.h"
31#include "core/network/sockets.h" 34#include "core/network/sockets.h"
32 35
33namespace Network { 36namespace Network {
@@ -47,11 +50,6 @@ void Finalize() {
47 WSACleanup(); 50 WSACleanup();
48} 51}
49 52
50constexpr IPv4Address TranslateIPv4(in_addr addr) {
51 auto& bytes = addr.S_un.S_un_b;
52 return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
53}
54
55sockaddr TranslateFromSockAddrIn(SockAddrIn input) { 53sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
56 sockaddr_in result; 54 sockaddr_in result;
57 55
@@ -138,12 +136,6 @@ void Initialize() {}
138 136
139void Finalize() {} 137void Finalize() {}
140 138
141constexpr IPv4Address TranslateIPv4(in_addr addr) {
142 const u32 bytes = addr.s_addr;
143 return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
144 static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
145}
146
147sockaddr TranslateFromSockAddrIn(SockAddrIn input) { 139sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
148 sockaddr_in result; 140 sockaddr_in result;
149 141
@@ -182,7 +174,7 @@ linger MakeLinger(bool enable, u32 linger_value) {
182} 174}
183 175
184bool EnableNonBlock(int fd, bool enable) { 176bool EnableNonBlock(int fd, bool enable) {
185 int flags = fcntl(fd, F_GETFD); 177 int flags = fcntl(fd, F_GETFL);
186 if (flags == -1) { 178 if (flags == -1) {
187 return false; 179 return false;
188 } 180 }
@@ -191,7 +183,7 @@ bool EnableNonBlock(int fd, bool enable) {
191 } else { 183 } else {
192 flags &= ~O_NONBLOCK; 184 flags &= ~O_NONBLOCK;
193 } 185 }
194 return fcntl(fd, F_SETFD, flags) == 0; 186 return fcntl(fd, F_SETFL, flags) == 0;
195} 187}
196 188
197Errno TranslateNativeError(int e) { 189Errno TranslateNativeError(int e) {
@@ -227,8 +219,12 @@ Errno GetAndLogLastError() {
227#else 219#else
228 int e = errno; 220 int e = errno;
229#endif 221#endif
222 const Errno err = TranslateNativeError(e);
223 if (err == Errno::AGAIN) {
224 return err;
225 }
230 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e)); 226 LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
231 return TranslateNativeError(e); 227 return err;
232} 228}
233 229
234int TranslateDomain(Domain domain) { 230int TranslateDomain(Domain domain) {
@@ -353,27 +349,29 @@ NetworkInstance::~NetworkInstance() {
353 Finalize(); 349 Finalize();
354} 350}
355 351
356std::pair<IPv4Address, Errno> GetHostIPv4Address() { 352std::optional<IPv4Address> GetHostIPv4Address() {
357 std::array<char, 256> name{}; 353 const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
358 if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) { 354 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
359 return {IPv4Address{}, GetAndLogLastError()}; 355 if (network_interfaces.size() == 0) {
356 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
357 return {};
360 } 358 }
361 359
362 hostent* const ent = gethostbyname(name.data()); 360 const auto res =
363 if (!ent) { 361 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
364 return {IPv4Address{}, GetAndLogLastError()}; 362 return iface.name == selected_network_interface;
365 } 363 });
366 if (ent->h_addr_list == nullptr) { 364
367 UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list"); 365 if (res != network_interfaces.end()) {
368 return {IPv4Address{}, Errno::SUCCESS}; 366 char ip_addr[16] = {};
369 } 367 ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
370 if (ent->h_length != sizeof(in_addr)) { 368 LOG_INFO(Network, "IP address: {}", ip_addr);
371 UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length);
372 }
373 369
374 in_addr addr; 370 return TranslateIPv4(res->ip_address);
375 std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr)); 371 } else {
376 return {TranslateIPv4(addr), Errno::SUCCESS}; 372 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
373 return {};
374 }
377} 375}
378 376
379std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { 377std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
diff --git a/src/core/network/network.h b/src/core/network/network.h
index bd30f1899..fdd3e4655 100644
--- a/src/core/network/network.h
+++ b/src/core/network/network.h
@@ -5,11 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <optional>
8#include <utility> 9#include <utility>
9 10
10#include "common/common_funcs.h" 11#include "common/common_funcs.h"
11#include "common/common_types.h" 12#include "common/common_types.h"
12 13
14#ifdef _WIN32
15#include <winsock2.h>
16#elif YUZU_UNIX
17#include <netinet/in.h>
18#endif
19
13namespace Network { 20namespace Network {
14 21
15class Socket; 22class Socket;
@@ -92,8 +99,21 @@ public:
92 ~NetworkInstance(); 99 ~NetworkInstance();
93}; 100};
94 101
102#ifdef _WIN32
103constexpr IPv4Address TranslateIPv4(in_addr addr) {
104 auto& bytes = addr.S_un.S_un_b;
105 return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
106}
107#elif YUZU_UNIX
108constexpr IPv4Address TranslateIPv4(in_addr addr) {
109 const u32 bytes = addr.s_addr;
110 return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
111 static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
112}
113#endif
114
95/// @brief Returns host's IPv4 address 115/// @brief Returns host's IPv4 address
96/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code 116/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
97std::pair<IPv4Address, Errno> GetHostIPv4Address(); 117std::optional<IPv4Address> GetHostIPv4Address();
98 118
99} // namespace Network 119} // namespace Network
diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp
new file mode 100644
index 000000000..6811f21b1
--- /dev/null
+++ b/src/core/network/network_interface.cpp
@@ -0,0 +1,210 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <fstream>
7#include <sstream>
8#include <vector>
9
10#include "common/bit_cast.h"
11#include "common/common_types.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "common/string_util.h"
15#include "core/network/network_interface.h"
16
17#ifdef _WIN32
18#include <iphlpapi.h>
19#else
20#include <cerrno>
21#include <ifaddrs.h>
22#include <net/if.h>
23#endif
24
25namespace Network {
26
27#ifdef _WIN32
28
29std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
30 std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses;
31 DWORD ret = ERROR_BUFFER_OVERFLOW;
32 DWORD buf_size = 0;
33
34 // retry up to 5 times
35 for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) {
36 ret = GetAdaptersAddresses(
37 AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS,
38 nullptr, adapter_addresses.data(), &buf_size);
39
40 if (ret != ERROR_BUFFER_OVERFLOW) {
41 break;
42 }
43
44 adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
45 }
46
47 if (ret != NO_ERROR) {
48 LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
49 return {};
50 }
51
52 std::vector<NetworkInterface> result;
53
54 for (auto current_address = adapter_addresses.data(); current_address != nullptr;
55 current_address = current_address->Next) {
56 if (current_address->FirstUnicastAddress == nullptr ||
57 current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
58 continue;
59 }
60
61 if (current_address->OperStatus != IfOperStatusUp) {
62 continue;
63 }
64
65 const auto ip_addr = Common::BitCast<struct sockaddr_in>(
66 *current_address->FirstUnicastAddress->Address.lpSockaddr)
67 .sin_addr;
68
69 ULONG mask = 0;
70 if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength,
71 &mask) != NO_ERROR) {
72 LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
73 continue;
74 }
75
76 struct in_addr gateway = {.S_un{.S_addr{0}}};
77 if (current_address->FirstGatewayAddress != nullptr &&
78 current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) {
79 gateway = Common::BitCast<struct sockaddr_in>(
80 *current_address->FirstGatewayAddress->Address.lpSockaddr)
81 .sin_addr;
82 }
83
84 result.emplace_back(NetworkInterface{
85 .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
86 .ip_address{ip_addr},
87 .subnet_mask = in_addr{.S_un{.S_addr{mask}}},
88 .gateway = gateway});
89 }
90
91 return result;
92}
93
94#else
95
96std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
97 struct ifaddrs* ifaddr = nullptr;
98
99 if (getifaddrs(&ifaddr) != 0) {
100 LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
101 std::strerror(errno));
102 return {};
103 }
104
105 std::vector<NetworkInterface> result;
106
107 for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
108 if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) {
109 continue;
110 }
111
112 if (ifa->ifa_addr->sa_family != AF_INET) {
113 continue;
114 }
115
116 if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) {
117 continue;
118 }
119
120 u32 gateway{};
121
122 std::ifstream file{"/proc/net/route"};
123 if (!file.is_open()) {
124 LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
125
126 result.emplace_back(NetworkInterface{
127 .name{ifa->ifa_name},
128 .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
129 .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
130 .gateway{in_addr{.s_addr = gateway}}});
131 continue;
132 }
133
134 // ignore header
135 file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
136
137 bool gateway_found = false;
138
139 for (std::string line; std::getline(file, line);) {
140 std::istringstream iss{line};
141
142 std::string iface_name;
143 iss >> iface_name;
144 if (iface_name != ifa->ifa_name) {
145 continue;
146 }
147
148 iss >> std::hex;
149
150 u32 dest{};
151 iss >> dest;
152 if (dest != 0) {
153 // not the default route
154 continue;
155 }
156
157 iss >> gateway;
158
159 u16 flags{};
160 iss >> flags;
161
162 // flag RTF_GATEWAY (defined in <linux/route.h>)
163 if ((flags & 0x2) == 0) {
164 continue;
165 }
166
167 gateway_found = true;
168 break;
169 }
170
171 if (!gateway_found) {
172 gateway = 0;
173 }
174
175 result.emplace_back(NetworkInterface{
176 .name{ifa->ifa_name},
177 .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
178 .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
179 .gateway{in_addr{.s_addr = gateway}}});
180 }
181
182 freeifaddrs(ifaddr);
183
184 return result;
185}
186
187#endif
188
189std::optional<NetworkInterface> GetSelectedNetworkInterface() {
190 const auto& selected_network_interface = Settings::values.network_interface.GetValue();
191 const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
192 if (network_interfaces.size() == 0) {
193 LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
194 return std::nullopt;
195 }
196
197 const auto res =
198 std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
199 return iface.name == selected_network_interface;
200 });
201
202 if (res == network_interfaces.end()) {
203 LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
204 return std::nullopt;
205 }
206
207 return *res;
208}
209
210} // namespace Network
diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h
new file mode 100644
index 000000000..980edb2f5
--- /dev/null
+++ b/src/core/network/network_interface.h
@@ -0,0 +1,29 @@
1// Copyright 2021 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <optional>
8#include <string>
9#include <vector>
10
11#ifdef _WIN32
12#include <winsock2.h>
13#else
14#include <netinet/in.h>
15#endif
16
17namespace Network {
18
19struct NetworkInterface {
20 std::string name;
21 struct in_addr ip_address;
22 struct in_addr subnet_mask;
23 struct in_addr gateway;
24};
25
26std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
27std::optional<NetworkInterface> GetSelectedNetworkInterface();
28
29} // namespace Network
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index fb8c02a77..14c77f162 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -298,14 +298,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
298 if (IR::IsGeneric(attr)) { 298 if (IR::IsGeneric(attr)) {
299 const u32 index{IR::GenericAttributeIndex(attr)}; 299 const u32 index{IR::GenericAttributeIndex(attr)};
300 const std::optional<AttrInfo> type{AttrTypes(ctx, index)}; 300 const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
301 if (!type) { 301 if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
302 // Attribute is disabled 302 // Attribute is disabled or varying component is not written
303 return ctx.Const(element == 3 ? 1.0f : 0.0f); 303 return ctx.Const(element == 3 ? 1.0f : 0.0f);
304 } 304 }
305 if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
306 // Varying component is not written
307 return ctx.Const(type && element == 3 ? 1.0f : 0.0f);
308 }
309 const Id generic_id{ctx.input_generics.at(index)}; 305 const Id generic_id{ctx.input_generics.at(index)};
310 const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))}; 306 const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))};
311 const Id value{ctx.OpLoad(type->id, pointer)}; 307 const Id value{ctx.OpLoad(type->id, pointer)};
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp
index 4c0f9654f..e31ca3544 100644
--- a/src/tests/common/param_package.cpp
+++ b/src/tests/common/param_package.cpp
@@ -4,11 +4,13 @@
4 4
5#include <catch2/catch.hpp> 5#include <catch2/catch.hpp>
6#include <math.h> 6#include <math.h>
7#include "common/logging/backend.h"
7#include "common/param_package.h" 8#include "common/param_package.h"
8 9
9namespace Common { 10namespace Common {
10 11
11TEST_CASE("ParamPackage", "[common]") { 12TEST_CASE("ParamPackage", "[common]") {
13 Common::Log::DisableLoggingInTests();
12 ParamPackage original{ 14 ParamPackage original{
13 {"abc", "xyz"}, 15 {"abc", "xyz"},
14 {"def", "42"}, 16 {"def", "42"},
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index 70030066a..d7e749485 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -742,6 +742,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
742 uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q); 742 uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q);
743 uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q); 743 uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q);
744 744
745 ASSERT(!current_frame_info.segment_enabled);
745 uncomp_writer.WriteBit(false); // Segmentation enabled (TODO). 746 uncomp_writer.WriteBit(false); // Segmentation enabled (TODO).
746 747
747 const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width); 748 const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width);
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
index 87eafdb03..3b1ed4b3a 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -22,7 +22,7 @@ struct Vp9FrameDimensions {
22}; 22};
23static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size"); 23static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
24 24
25enum FrameFlags : u32 { 25enum class FrameFlags : u32 {
26 IsKeyFrame = 1 << 0, 26 IsKeyFrame = 1 << 0,
27 LastFrameIsKeyFrame = 1 << 1, 27 LastFrameIsKeyFrame = 1 << 1,
28 FrameSizeChanged = 1 << 2, 28 FrameSizeChanged = 1 << 2,
@@ -30,6 +30,7 @@ enum FrameFlags : u32 {
30 LastShowFrame = 1 << 4, 30 LastShowFrame = 1 << 4,
31 IntraOnly = 1 << 5, 31 IntraOnly = 1 << 5,
32}; 32};
33DECLARE_ENUM_FLAG_OPERATORS(FrameFlags)
33 34
34enum class TxSize { 35enum class TxSize {
35 Tx4x4 = 0, // 4x4 transform 36 Tx4x4 = 0, // 4x4 transform
@@ -92,44 +93,34 @@ struct Vp9EntropyProbs {
92static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size"); 93static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size");
93 94
94struct Vp9PictureInfo { 95struct Vp9PictureInfo {
95 bool is_key_frame; 96 u32 bitstream_size;
96 bool intra_only; 97 std::array<u64, 4> frame_offsets;
97 bool last_frame_was_key;
98 bool frame_size_changed;
99 bool error_resilient_mode;
100 bool last_frame_shown;
101 bool show_frame;
102 std::array<s8, 4> ref_frame_sign_bias; 98 std::array<s8, 4> ref_frame_sign_bias;
103 s32 base_q_index; 99 s32 base_q_index;
104 s32 y_dc_delta_q; 100 s32 y_dc_delta_q;
105 s32 uv_dc_delta_q; 101 s32 uv_dc_delta_q;
106 s32 uv_ac_delta_q; 102 s32 uv_ac_delta_q;
107 bool lossless;
108 s32 transform_mode; 103 s32 transform_mode;
109 bool allow_high_precision_mv;
110 s32 interp_filter; 104 s32 interp_filter;
111 s32 reference_mode; 105 s32 reference_mode;
112 s8 comp_fixed_ref;
113 std::array<s8, 2> comp_var_ref;
114 s32 log2_tile_cols; 106 s32 log2_tile_cols;
115 s32 log2_tile_rows; 107 s32 log2_tile_rows;
116 bool segment_enabled;
117 bool segment_map_update;
118 bool segment_map_temporal_update;
119 s32 segment_abs_delta;
120 std::array<u32, 8> segment_feature_enable;
121 std::array<std::array<s16, 4>, 8> segment_feature_data;
122 bool mode_ref_delta_enabled;
123 bool use_prev_in_find_mv_refs;
124 std::array<s8, 4> ref_deltas; 108 std::array<s8, 4> ref_deltas;
125 std::array<s8, 2> mode_deltas; 109 std::array<s8, 2> mode_deltas;
126 Vp9EntropyProbs entropy; 110 Vp9EntropyProbs entropy;
127 Vp9FrameDimensions frame_size; 111 Vp9FrameDimensions frame_size;
128 u8 first_level; 112 u8 first_level;
129 u8 sharpness_level; 113 u8 sharpness_level;
130 u32 bitstream_size; 114 bool is_key_frame;
131 std::array<u64, 4> frame_offsets; 115 bool intra_only;
132 std::array<bool, 4> refresh_frame; 116 bool last_frame_was_key;
117 bool error_resilient_mode;
118 bool last_frame_shown;
119 bool show_frame;
120 bool lossless;
121 bool allow_high_precision_mv;
122 bool segment_enabled;
123 bool mode_ref_delta_enabled;
133}; 124};
134 125
135struct Vp9FrameContainer { 126struct Vp9FrameContainer {
@@ -145,7 +136,7 @@ struct PictureInfo {
145 Vp9FrameDimensions golden_frame_size; ///< 0x50 136 Vp9FrameDimensions golden_frame_size; ///< 0x50
146 Vp9FrameDimensions alt_frame_size; ///< 0x58 137 Vp9FrameDimensions alt_frame_size; ///< 0x58
147 Vp9FrameDimensions current_frame_size; ///< 0x60 138 Vp9FrameDimensions current_frame_size; ///< 0x60
148 u32 vp9_flags; ///< 0x68 139 FrameFlags vp9_flags; ///< 0x68
149 std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C 140 std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C
150 u8 first_level; ///< 0x70 141 u8 first_level; ///< 0x70
151 u8 sharpness_level; ///< 0x71 142 u8 sharpness_level; ///< 0x71
@@ -158,60 +149,43 @@ struct PictureInfo {
158 u8 allow_high_precision_mv; ///< 0x78 149 u8 allow_high_precision_mv; ///< 0x78
159 u8 interp_filter; ///< 0x79 150 u8 interp_filter; ///< 0x79
160 u8 reference_mode; ///< 0x7A 151 u8 reference_mode; ///< 0x7A
161 s8 comp_fixed_ref; ///< 0x7B 152 INSERT_PADDING_BYTES_NOINIT(3); ///< 0x7B
162 std::array<s8, 2> comp_var_ref; ///< 0x7C
163 u8 log2_tile_cols; ///< 0x7E 153 u8 log2_tile_cols; ///< 0x7E
164 u8 log2_tile_rows; ///< 0x7F 154 u8 log2_tile_rows; ///< 0x7F
165 Segmentation segmentation; ///< 0x80 155 Segmentation segmentation; ///< 0x80
166 LoopFilter loop_filter; ///< 0xE4 156 LoopFilter loop_filter; ///< 0xE4
167 INSERT_PADDING_BYTES_NOINIT(5); ///< 0xEB 157 INSERT_PADDING_BYTES_NOINIT(21); ///< 0xEB
168 u32 surface_params; ///< 0xF0
169 INSERT_PADDING_WORDS_NOINIT(3); ///< 0xF4
170 158
171 [[nodiscard]] Vp9PictureInfo Convert() const { 159 [[nodiscard]] Vp9PictureInfo Convert() const {
172 return { 160 return {
173 .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0, 161 .bitstream_size = bitstream_size,
174 .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0, 162 .frame_offsets{},
175 .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0,
176 .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
177 .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
178 .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
179 .show_frame = true,
180 .ref_frame_sign_bias = ref_frame_sign_bias, 163 .ref_frame_sign_bias = ref_frame_sign_bias,
181 .base_q_index = base_q_index, 164 .base_q_index = base_q_index,
182 .y_dc_delta_q = y_dc_delta_q, 165 .y_dc_delta_q = y_dc_delta_q,
183 .uv_dc_delta_q = uv_dc_delta_q, 166 .uv_dc_delta_q = uv_dc_delta_q,
184 .uv_ac_delta_q = uv_ac_delta_q, 167 .uv_ac_delta_q = uv_ac_delta_q,
185 .lossless = lossless != 0,
186 .transform_mode = tx_mode, 168 .transform_mode = tx_mode,
187 .allow_high_precision_mv = allow_high_precision_mv != 0,
188 .interp_filter = interp_filter, 169 .interp_filter = interp_filter,
189 .reference_mode = reference_mode, 170 .reference_mode = reference_mode,
190 .comp_fixed_ref = comp_fixed_ref,
191 .comp_var_ref = comp_var_ref,
192 .log2_tile_cols = log2_tile_cols, 171 .log2_tile_cols = log2_tile_cols,
193 .log2_tile_rows = log2_tile_rows, 172 .log2_tile_rows = log2_tile_rows,
194 .segment_enabled = segmentation.enabled != 0,
195 .segment_map_update = segmentation.update_map != 0,
196 .segment_map_temporal_update = segmentation.temporal_update != 0,
197 .segment_abs_delta = segmentation.abs_delta,
198 .segment_feature_enable = segmentation.feature_mask,
199 .segment_feature_data = segmentation.feature_data,
200 .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
201 .use_prev_in_find_mv_refs = !(vp9_flags == (FrameFlags::ErrorResilientMode)) &&
202 !(vp9_flags == (FrameFlags::FrameSizeChanged)) &&
203 !(vp9_flags == (FrameFlags::IntraOnly)) &&
204 (vp9_flags == (FrameFlags::LastShowFrame)) &&
205 !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)),
206 .ref_deltas = loop_filter.ref_deltas, 173 .ref_deltas = loop_filter.ref_deltas,
207 .mode_deltas = loop_filter.mode_deltas, 174 .mode_deltas = loop_filter.mode_deltas,
208 .entropy{}, 175 .entropy{},
209 .frame_size = current_frame_size, 176 .frame_size = current_frame_size,
210 .first_level = first_level, 177 .first_level = first_level,
211 .sharpness_level = sharpness_level, 178 .sharpness_level = sharpness_level,
212 .bitstream_size = bitstream_size, 179 .is_key_frame = True(vp9_flags & FrameFlags::IsKeyFrame),
213 .frame_offsets{}, 180 .intra_only = True(vp9_flags & FrameFlags::IntraOnly),
214 .refresh_frame{}, 181 .last_frame_was_key = True(vp9_flags & FrameFlags::LastFrameIsKeyFrame),
182 .error_resilient_mode = True(vp9_flags & FrameFlags::ErrorResilientMode),
183 .last_frame_shown = True(vp9_flags & FrameFlags::LastShowFrame),
184 .show_frame = true,
185 .lossless = lossless != 0,
186 .allow_high_precision_mv = allow_high_precision_mv != 0,
187 .segment_enabled = segmentation.enabled != 0,
188 .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
215 }; 189 };
216 } 190 }
217}; 191};
@@ -316,7 +290,6 @@ ASSERT_POSITION(last_frame_size, 0x48);
316ASSERT_POSITION(first_level, 0x70); 290ASSERT_POSITION(first_level, 0x70);
317ASSERT_POSITION(segmentation, 0x80); 291ASSERT_POSITION(segmentation, 0x80);
318ASSERT_POSITION(loop_filter, 0xE4); 292ASSERT_POSITION(loop_filter, 0xE4);
319ASSERT_POSITION(surface_params, 0xF0);
320#undef ASSERT_POSITION 293#undef ASSERT_POSITION
321 294
322#define ASSERT_POSITION(field_name, position) \ 295#define ASSERT_POSITION(field_name, position) \
diff --git a/src/video_core/macro/macro_jit_x64.h b/src/video_core/macro/macro_jit_x64.h
index 7f50ac2f8..d03d480b4 100644
--- a/src/video_core/macro/macro_jit_x64.h
+++ b/src/video_core/macro/macro_jit_x64.h
@@ -6,7 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <bitset> 8#include <bitset>
9#include <xbyak.h> 9#include <xbyak/xbyak.h>
10#include "common/bit_field.h" 10#include "common/bit_field.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/x64/xbyak_abi.h" 12#include "common/x64/xbyak_abi.h"
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 882eff880..c60ed6453 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -463,6 +463,7 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
463 ++page_index; 463 ++page_index;
464 page_offset = 0; 464 page_offset = 0;
465 remaining_size -= num_bytes; 465 remaining_size -= num_bytes;
466 old_page_addr = page_addr;
466 } 467 }
467 split(); 468 split();
468 return result; 469 return result;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index a37ca1fdf..f316c4f92 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -281,7 +281,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw
281 .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, 281 .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U,
282 .unified_descriptor_binding = true, 282 .unified_descriptor_binding = true,
283 .support_descriptor_aliasing = true, 283 .support_descriptor_aliasing = true,
284 .support_int8 = true, 284 .support_int8 = device.IsInt8Supported(),
285 .support_int16 = device.IsShaderInt16Supported(), 285 .support_int16 = device.IsShaderInt16Supported(),
286 .support_int64 = device.IsShaderInt64Supported(), 286 .support_int64 = device.IsShaderInt64Supported(),
287 .support_vertex_instance_id = false, 287 .support_vertex_instance_id = false,
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index d2c4a7fcf..24e943e4c 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -76,56 +76,31 @@ template <bool TO_LINEAR>
76void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width, 76void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
77 u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) { 77 u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) {
78 switch (bytes_per_pixel) { 78 switch (bytes_per_pixel) {
79 case 1: 79#define BPP_CASE(x) \
80 return SwizzleImpl<TO_LINEAR, 1>(output, input, width, height, depth, block_height, 80 case x: \
81 return SwizzleImpl<TO_LINEAR, x>(output, input, width, height, depth, block_height, \
81 block_depth, stride_alignment); 82 block_depth, stride_alignment);
82 case 2: 83 BPP_CASE(1)
83 return SwizzleImpl<TO_LINEAR, 2>(output, input, width, height, depth, block_height, 84 BPP_CASE(2)
84 block_depth, stride_alignment); 85 BPP_CASE(3)
85 case 3: 86 BPP_CASE(4)
86 return SwizzleImpl<TO_LINEAR, 3>(output, input, width, height, depth, block_height, 87 BPP_CASE(6)
87 block_depth, stride_alignment); 88 BPP_CASE(8)
88 case 4: 89 BPP_CASE(12)
89 return SwizzleImpl<TO_LINEAR, 4>(output, input, width, height, depth, block_height, 90 BPP_CASE(16)
90 block_depth, stride_alignment); 91#undef BPP_CASE
91 case 6:
92 return SwizzleImpl<TO_LINEAR, 6>(output, input, width, height, depth, block_height,
93 block_depth, stride_alignment);
94 case 8:
95 return SwizzleImpl<TO_LINEAR, 8>(output, input, width, height, depth, block_height,
96 block_depth, stride_alignment);
97 case 12:
98 return SwizzleImpl<TO_LINEAR, 12>(output, input, width, height, depth, block_height,
99 block_depth, stride_alignment);
100 case 16:
101 return SwizzleImpl<TO_LINEAR, 16>(output, input, width, height, depth, block_height,
102 block_depth, stride_alignment);
103 default: 92 default:
104 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel); 93 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
105 } 94 }
106} 95}
107} // Anonymous namespace
108
109void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
110 u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth,
111 u32 stride_alignment) {
112 Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
113 stride_alignment);
114}
115
116void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
117 u32 height, u32 depth, u32 block_height, u32 block_depth,
118 u32 stride_alignment) {
119 Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
120 stride_alignment);
121}
122 96
97template <u32 BYTES_PER_PIXEL>
123void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width, 98void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
124 u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data, 99 u8* swizzled_data, const u8* unswizzled_data, u32 block_height_bit,
125 u32 block_height_bit, u32 offset_x, u32 offset_y) { 100 u32 offset_x, u32 offset_y) {
126 const u32 block_height = 1U << block_height_bit; 101 const u32 block_height = 1U << block_height_bit;
127 const u32 image_width_in_gobs = 102 const u32 image_width_in_gobs =
128 (swizzled_width * bytes_per_pixel + (GOB_SIZE_X - 1)) / GOB_SIZE_X; 103 (swizzled_width * BYTES_PER_PIXEL + (GOB_SIZE_X - 1)) / GOB_SIZE_X;
129 for (u32 line = 0; line < subrect_height; ++line) { 104 for (u32 line = 0; line < subrect_height; ++line) {
130 const u32 dst_y = line + offset_y; 105 const u32 dst_y = line + offset_y;
131 const u32 gob_address_y = 106 const u32 gob_address_y =
@@ -135,20 +110,21 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
135 for (u32 x = 0; x < subrect_width; ++x) { 110 for (u32 x = 0; x < subrect_width; ++x) {
136 const u32 dst_x = x + offset_x; 111 const u32 dst_x = x + offset_x;
137 const u32 gob_address = 112 const u32 gob_address =
138 gob_address_y + (dst_x * bytes_per_pixel / GOB_SIZE_X) * GOB_SIZE * block_height; 113 gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height;
139 const u32 swizzled_offset = gob_address + table[(dst_x * bytes_per_pixel) % GOB_SIZE_X]; 114 const u32 swizzled_offset = gob_address + table[(dst_x * BYTES_PER_PIXEL) % GOB_SIZE_X];
140 const u32 unswizzled_offset = line * source_pitch + x * bytes_per_pixel; 115 const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL;
141 116
142 const u8* const source_line = unswizzled_data + unswizzled_offset; 117 const u8* const source_line = unswizzled_data + unswizzled_offset;
143 u8* const dest_addr = swizzled_data + swizzled_offset; 118 u8* const dest_addr = swizzled_data + swizzled_offset;
144 std::memcpy(dest_addr, source_line, bytes_per_pixel); 119 std::memcpy(dest_addr, source_line, BYTES_PER_PIXEL);
145 } 120 }
146 } 121 }
147} 122}
148 123
149void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel, 124template <u32 BYTES_PER_PIXEL>
150 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) { 125void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 block_height,
151 const u32 stride = width * bytes_per_pixel; 126 u32 origin_x, u32 origin_y, u8* output, const u8* input) {
127 const u32 stride = width * BYTES_PER_PIXEL;
152 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; 128 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
153 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height); 129 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height);
154 130
@@ -163,24 +139,25 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width,
163 const u32 src_offset_y = (block_y >> block_height) * block_size + 139 const u32 src_offset_y = (block_y >> block_height) * block_size +
164 ((block_y & block_height_mask) << GOB_SIZE_SHIFT); 140 ((block_y & block_height_mask) << GOB_SIZE_SHIFT);
165 for (u32 column = 0; column < line_length_in; ++column) { 141 for (u32 column = 0; column < line_length_in; ++column) {
166 const u32 src_x = (column + origin_x) * bytes_per_pixel; 142 const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL;
167 const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift; 143 const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift;
168 144
169 const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X]; 145 const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X];
170 const u32 unswizzled_offset = line * pitch + column * bytes_per_pixel; 146 const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL;
171 147
172 std::memcpy(output + unswizzled_offset, input + swizzled_offset, bytes_per_pixel); 148 std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL);
173 } 149 }
174 } 150 }
175} 151}
176 152
153template <u32 BYTES_PER_PIXEL>
177void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height, 154void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
178 u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x, 155 u32 block_height, u32 block_depth, u32 origin_x, u32 origin_y, u8* output,
179 u32 origin_y, u8* output, const u8* input) { 156 const u8* input) {
180 UNIMPLEMENTED_IF(origin_x > 0); 157 UNIMPLEMENTED_IF(origin_x > 0);
181 UNIMPLEMENTED_IF(origin_y > 0); 158 UNIMPLEMENTED_IF(origin_y > 0);
182 159
183 const u32 stride = width * bytes_per_pixel; 160 const u32 stride = width * BYTES_PER_PIXEL;
184 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X; 161 const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
185 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth); 162 const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
186 163
@@ -195,11 +172,93 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
195 for (u32 x = 0; x < line_length_in; ++x) { 172 for (u32 x = 0; x < line_length_in; ++x) {
196 const u32 dst_offset = 173 const u32 dst_offset =
197 ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X]; 174 ((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X];
198 const u32 src_offset = x * bytes_per_pixel + line * pitch; 175 const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch;
199 std::memcpy(output + dst_offset, input + src_offset, bytes_per_pixel); 176 std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL);
200 } 177 }
201 } 178 }
202} 179}
180} // Anonymous namespace
181
182void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
183 u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth,
184 u32 stride_alignment) {
185 Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
186 stride_alignment);
187}
188
189void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
190 u32 height, u32 depth, u32 block_height, u32 block_depth,
191 u32 stride_alignment) {
192 Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
193 stride_alignment);
194}
195
196void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
197 u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data,
198 u32 block_height_bit, u32 offset_x, u32 offset_y) {
199 switch (bytes_per_pixel) {
200#define BPP_CASE(x) \
201 case x: \
202 return SwizzleSubrect<x>(subrect_width, subrect_height, source_pitch, swizzled_width, \
203 swizzled_data, unswizzled_data, block_height_bit, offset_x, \
204 offset_y);
205 BPP_CASE(1)
206 BPP_CASE(2)
207 BPP_CASE(3)
208 BPP_CASE(4)
209 BPP_CASE(6)
210 BPP_CASE(8)
211 BPP_CASE(12)
212 BPP_CASE(16)
213#undef BPP_CASE
214 default:
215 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
216 }
217}
218
219void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel,
220 u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) {
221 switch (bytes_per_pixel) {
222#define BPP_CASE(x) \
223 case x: \
224 return UnswizzleSubrect<x>(line_length_in, line_count, pitch, width, block_height, \
225 origin_x, origin_y, output, input);
226 BPP_CASE(1)
227 BPP_CASE(2)
228 BPP_CASE(3)
229 BPP_CASE(4)
230 BPP_CASE(6)
231 BPP_CASE(8)
232 BPP_CASE(12)
233 BPP_CASE(16)
234#undef BPP_CASE
235 default:
236 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
237 }
238}
239
240void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
241 u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
242 u32 origin_y, u8* output, const u8* input) {
243 switch (bytes_per_pixel) {
244#define BPP_CASE(x) \
245 case x: \
246 return SwizzleSliceToVoxel<x>(line_length_in, line_count, pitch, width, height, \
247 block_height, block_depth, origin_x, origin_y, output, \
248 input);
249 BPP_CASE(1)
250 BPP_CASE(2)
251 BPP_CASE(3)
252 BPP_CASE(4)
253 BPP_CASE(6)
254 BPP_CASE(8)
255 BPP_CASE(12)
256 BPP_CASE(16)
257#undef BPP_CASE
258 default:
259 UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
260 }
261}
203 262
204void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y, 263void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y,
205 const u32 block_height_bit, const std::size_t copy_size, const u8* source_data, 264 const u32 block_height_bit, const std::size_t copy_size, const u8* source_data,
@@ -220,7 +279,7 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32
220 u8* dest_addr = swizzle_data + swizzled_offset; 279 u8* dest_addr = swizzle_data + swizzled_offset;
221 count++; 280 count++;
222 281
223 std::memcpy(dest_addr, source_line, 1); 282 *dest_addr = *source_line;
224 } 283 }
225 } 284 }
226} 285}
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 1a9399455..7994cb859 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -159,7 +159,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
159 return {raw, raw}; 159 return {raw, raw};
160 } else { 160 } else {
161 const Tegra::Texture::TextureHandle handle{raw}; 161 const Tegra::Texture::TextureHandle handle{raw};
162 return {handle.tic_id, via_header_index ? handle.tic_id : handle.tsc_id}; 162 return {handle.tic_id, handle.tsc_id};
163 } 163 }
164} 164}
165 165
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 8e56a89e1..86ca4be54 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -368,18 +368,21 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
368 }; 368 };
369 SetNext(next, demote); 369 SetNext(next, demote);
370 370
371 VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8; 371 if (is_int8_supported || is_float16_supported) {
372 if (is_float16_supported) { 372 VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8{
373 float16_int8 = {
374 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR, 373 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR,
375 .pNext = nullptr, 374 .pNext = nullptr,
376 .shaderFloat16 = true, 375 .shaderFloat16 = is_float16_supported,
377 .shaderInt8 = false, 376 .shaderInt8 = is_int8_supported,
378 }; 377 };
379 SetNext(next, float16_int8); 378 SetNext(next, float16_int8);
380 } else { 379 }
380 if (!is_float16_supported) {
381 LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively"); 381 LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively");
382 } 382 }
383 if (!is_int8_supported) {
384 LOG_INFO(Render_Vulkan, "Device doesn't support int8 natively");
385 }
383 386
384 if (!nv_viewport_swizzle) { 387 if (!nv_viewport_swizzle) {
385 LOG_INFO(Render_Vulkan, "Device doesn't support viewport swizzles"); 388 LOG_INFO(Render_Vulkan, "Device doesn't support viewport swizzles");
@@ -909,6 +912,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
909 912
910 physical.GetFeatures2KHR(features); 913 physical.GetFeatures2KHR(features);
911 is_float16_supported = float16_int8_features.shaderFloat16; 914 is_float16_supported = float16_int8_features.shaderFloat16;
915 is_int8_supported = float16_int8_features.shaderInt8;
912 extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); 916 extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
913 } 917 }
914 if (has_ext_subgroup_size_control) { 918 if (has_ext_subgroup_size_control) {
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index c19f40746..234d74129 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -139,11 +139,16 @@ public:
139 return is_optimal_astc_supported; 139 return is_optimal_astc_supported;
140 } 140 }
141 141
142 /// Returns true if the device supports float16 natively 142 /// Returns true if the device supports float16 natively.
143 bool IsFloat16Supported() const { 143 bool IsFloat16Supported() const {
144 return is_float16_supported; 144 return is_float16_supported;
145 } 145 }
146 146
147 /// Returns true if the device supports int8 natively.
148 bool IsInt8Supported() const {
149 return is_int8_supported;
150 }
151
147 /// Returns true if the device warp size can potentially be bigger than guest's warp size. 152 /// Returns true if the device warp size can potentially be bigger than guest's warp size.
148 bool IsWarpSizePotentiallyBiggerThanGuest() const { 153 bool IsWarpSizePotentiallyBiggerThanGuest() const {
149 return is_warp_potentially_bigger; 154 return is_warp_potentially_bigger;
@@ -367,7 +372,8 @@ private:
367 u64 device_access_memory{}; ///< Total size of device local memory in bytes. 372 u64 device_access_memory{}; ///< Total size of device local memory in bytes.
368 u32 max_push_descriptors{}; ///< Maximum number of push descriptors 373 u32 max_push_descriptors{}; ///< Maximum number of push descriptors
369 bool is_optimal_astc_supported{}; ///< Support for native ASTC. 374 bool is_optimal_astc_supported{}; ///< Support for native ASTC.
370 bool is_float16_supported{}; ///< Support for float16 arithmetics. 375 bool is_float16_supported{}; ///< Support for float16 arithmetic.
376 bool is_int8_supported{}; ///< Support for int8 arithmetic.
371 bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. 377 bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
372 bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. 378 bool is_formatless_image_load_supported{}; ///< Support for shader image read without format.
373 bool is_depth_bounds_supported{}; ///< Support for depth bounds. 379 bool is_depth_bounds_supported{}; ///< Support for depth bounds.
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index cb4bdcc7e..19ba0dbba 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -102,9 +102,9 @@ add_executable(yuzu
102 configuration/configure_profile_manager.cpp 102 configuration/configure_profile_manager.cpp
103 configuration/configure_profile_manager.h 103 configuration/configure_profile_manager.h
104 configuration/configure_profile_manager.ui 104 configuration/configure_profile_manager.ui
105 configuration/configure_service.cpp 105 configuration/configure_network.cpp
106 configuration/configure_service.h 106 configuration/configure_network.h
107 configuration/configure_service.ui 107 configuration/configure_network.ui
108 configuration/configure_system.cpp 108 configuration/configure_system.cpp
109 configuration/configure_system.h 109 configuration/configure_system.h
110 configuration/configure_system.ui 110 configuration/configure_system.ui
@@ -182,7 +182,14 @@ if (ENABLE_QT_TRANSLATION)
182 # Update source TS file if enabled 182 # Update source TS file if enabled
183 if (GENERATE_QT_TRANSLATION) 183 if (GENERATE_QT_TRANSLATION)
184 get_target_property(SRCS yuzu SOURCES) 184 get_target_property(SRCS yuzu SOURCES)
185 qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${YUZU_QT_LANGUAGES}/en.ts) 185 qt5_create_translation(QM_FILES
186 ${SRCS}
187 ${UIS}
188 ${YUZU_QT_LANGUAGES}/en.ts
189 OPTIONS
190 -source-language en_US
191 -target-language en_US
192 )
186 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts) 193 add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts)
187 endif() 194 endif()
188 195
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index 848801cec..8fc0c5a36 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -438,7 +438,7 @@ void QtSoftwareKeyboardDialog::ShowInlineKeyboard(
438 initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags; 438 initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags;
439 initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button; 439 initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button;
440 initialize_parameters.enable_return_button = appear_parameters.enable_return_button; 440 initialize_parameters.enable_return_button = appear_parameters.enable_return_button;
441 initialize_parameters.disable_cancel_button = initialize_parameters.disable_cancel_button; 441 initialize_parameters.disable_cancel_button = appear_parameters.disable_cancel_button;
442 442
443 SetKeyboardType(); 443 SetKeyboardType();
444 SetControllerImage(); 444 SetControllerImage();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 380379eb4..377795326 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -692,6 +692,7 @@ void Config::ReadServiceValues() {
692 qt_config->beginGroup(QStringLiteral("Services")); 692 qt_config->beginGroup(QStringLiteral("Services"));
693 ReadBasicSetting(Settings::values.bcat_backend); 693 ReadBasicSetting(Settings::values.bcat_backend);
694 ReadBasicSetting(Settings::values.bcat_boxcat_local); 694 ReadBasicSetting(Settings::values.bcat_boxcat_local);
695 ReadBasicSetting(Settings::values.network_interface);
695 qt_config->endGroup(); 696 qt_config->endGroup();
696} 697}
697 698
@@ -1144,7 +1145,7 @@ void Config::SaveValues() {
1144 SaveDataStorageValues(); 1145 SaveDataStorageValues();
1145 SaveDebuggingValues(); 1146 SaveDebuggingValues();
1146 SaveDisabledAddOnValues(); 1147 SaveDisabledAddOnValues();
1147 SaveServiceValues(); 1148 SaveNetworkValues();
1148 SaveUIValues(); 1149 SaveUIValues();
1149 SaveWebServiceValues(); 1150 SaveWebServiceValues();
1150 SaveMiscellaneousValues(); 1151 SaveMiscellaneousValues();
@@ -1238,11 +1239,12 @@ void Config::SaveDebuggingValues() {
1238 qt_config->endGroup(); 1239 qt_config->endGroup();
1239} 1240}
1240 1241
1241void Config::SaveServiceValues() { 1242void Config::SaveNetworkValues() {
1242 qt_config->beginGroup(QStringLiteral("Services")); 1243 qt_config->beginGroup(QStringLiteral("Services"));
1243 1244
1244 WriteBasicSetting(Settings::values.bcat_backend); 1245 WriteBasicSetting(Settings::values.bcat_backend);
1245 WriteBasicSetting(Settings::values.bcat_boxcat_local); 1246 WriteBasicSetting(Settings::values.bcat_boxcat_local);
1247 WriteBasicSetting(Settings::values.network_interface);
1246 1248
1247 qt_config->endGroup(); 1249 qt_config->endGroup();
1248} 1250}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index c1d7feb9f..9555f4498 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -88,7 +88,7 @@ private:
88 void SaveCoreValues(); 88 void SaveCoreValues();
89 void SaveDataStorageValues(); 89 void SaveDataStorageValues();
90 void SaveDebuggingValues(); 90 void SaveDebuggingValues();
91 void SaveServiceValues(); 91 void SaveNetworkValues();
92 void SaveDisabledAddOnValues(); 92 void SaveDisabledAddOnValues();
93 void SaveMiscellaneousValues(); 93 void SaveMiscellaneousValues();
94 void SavePathValues(); 94 void SavePathValues();
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index fca9aed5f..6258dcf20 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -147,12 +147,12 @@
147 <string>Web</string> 147 <string>Web</string>
148 </attribute> 148 </attribute>
149 </widget> 149 </widget>
150 <widget class="ConfigureService" name="serviceTab"> 150 <widget class="ConfigureNetwork" name="networkTab">
151 <property name="accessibleName"> 151 <property name="accessibleName">
152 <string>Services</string> 152 <string>Network</string>
153 </property> 153 </property>
154 <attribute name="title"> 154 <attribute name="title">
155 <string>Services</string> 155 <string>Network</string>
156 </attribute> 156 </attribute>
157 </widget> 157 </widget>
158 </widget> 158 </widget>
@@ -242,9 +242,9 @@
242 <container>1</container> 242 <container>1</container>
243 </customwidget> 243 </customwidget>
244 <customwidget> 244 <customwidget>
245 <class>ConfigureService</class> 245 <class>ConfigureNetwork</class>
246 <extends>QWidget</extends> 246 <extends>QWidget</extends>
247 <header>configuration/configure_service.h</header> 247 <header>configuration/configure_network.h</header>
248 <container>1</container> 248 <container>1</container>
249 </customwidget> 249 </customwidget>
250 <customwidget> 250 <customwidget>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index bc009b6b3..fe4186157 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -67,7 +67,7 @@ void ConfigureDialog::ApplyConfiguration() {
67 ui->audioTab->ApplyConfiguration(); 67 ui->audioTab->ApplyConfiguration();
68 ui->debugTab->ApplyConfiguration(); 68 ui->debugTab->ApplyConfiguration();
69 ui->webTab->ApplyConfiguration(); 69 ui->webTab->ApplyConfiguration();
70 ui->serviceTab->ApplyConfiguration(); 70 ui->networkTab->ApplyConfiguration();
71 Core::System::GetInstance().ApplySettings(); 71 Core::System::GetInstance().ApplySettings();
72 Settings::LogSettings(); 72 Settings::LogSettings();
73} 73}
@@ -103,7 +103,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
103void ConfigureDialog::PopulateSelectionList() { 103void ConfigureDialog::PopulateSelectionList() {
104 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{ 104 const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
105 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}}, 105 {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
106 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}}, 106 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->networkTab, ui->filesystemTab}},
107 {tr("CPU"), {ui->cpuTab}}, 107 {tr("CPU"), {ui->cpuTab}},
108 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}}, 108 {tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
109 {tr("Audio"), {ui->audioTab}}, 109 {tr("Audio"), {ui->audioTab}},
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index cd633e45f..9c890ed5d 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -647,18 +647,18 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
647 // Face buttons 647 // Face buttons
648 p.setPen(colors.outline); 648 p.setPen(colors.outline);
649 button_color = colors.button; 649 button_color = colors.button;
650 DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius); 650 DrawCircleButton(p, face_center + QPointF(face_distance, 0), button_values[A], face_radius);
651 DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius); 651 DrawCircleButton(p, face_center + QPointF(0, face_distance), button_values[B], face_radius);
652 DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius); 652 DrawCircleButton(p, face_center + QPointF(0, -face_distance), button_values[X], face_radius);
653 DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius); 653 DrawCircleButton(p, face_center + QPointF(-face_distance, 0), button_values[Y], face_radius);
654 654
655 // Face buttons text 655 // Face buttons text
656 p.setPen(colors.transparent); 656 p.setPen(colors.transparent);
657 p.setBrush(colors.font); 657 p.setBrush(colors.font);
658 DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size); 658 DrawSymbol(p, face_center + QPointF(face_distance, 0), Symbol::A, text_size);
659 DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size); 659 DrawSymbol(p, face_center + QPointF(0, face_distance), Symbol::B, text_size);
660 DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size); 660 DrawSymbol(p, face_center + QPointF(0, -face_distance), Symbol::X, text_size);
661 DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size); 661 DrawSymbol(p, face_center + QPointF(-face_distance, 1), Symbol::Y, text_size);
662 662
663 // D-pad constants 663 // D-pad constants
664 const QPointF dpad_center = center + QPoint(-171, 8); 664 const QPointF dpad_center = center + QPoint(-171, 8);
@@ -669,18 +669,20 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
669 // D-pad buttons 669 // D-pad buttons
670 p.setPen(colors.outline); 670 p.setPen(colors.outline);
671 button_color = colors.button; 671 button_color = colors.button;
672 DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius); 672 DrawCircleButton(p, dpad_center + QPointF(dpad_distance, 0), button_values[DRight],
673 DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius); 673 dpad_radius);
674 DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius); 674 DrawCircleButton(p, dpad_center + QPointF(0, dpad_distance), button_values[DDown], dpad_radius);
675 DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius); 675 DrawCircleButton(p, dpad_center + QPointF(0, -dpad_distance), button_values[DUp], dpad_radius);
676 DrawCircleButton(p, dpad_center + QPointF(-dpad_distance, 0), button_values[DLeft],
677 dpad_radius);
676 678
677 // D-pad arrows 679 // D-pad arrows
678 p.setPen(colors.font2); 680 p.setPen(colors.font2);
679 p.setBrush(colors.font2); 681 p.setBrush(colors.font2);
680 DrawArrow(p, dpad_center + QPoint(dpad_distance, 0), Direction::Right, dpad_arrow_size); 682 DrawArrow(p, dpad_center + QPointF(dpad_distance, 0), Direction::Right, dpad_arrow_size);
681 DrawArrow(p, dpad_center + QPoint(0, dpad_distance), Direction::Down, dpad_arrow_size); 683 DrawArrow(p, dpad_center + QPointF(0, dpad_distance), Direction::Down, dpad_arrow_size);
682 DrawArrow(p, dpad_center + QPoint(0, -dpad_distance), Direction::Up, dpad_arrow_size); 684 DrawArrow(p, dpad_center + QPointF(0, -dpad_distance), Direction::Up, dpad_arrow_size);
683 DrawArrow(p, dpad_center + QPoint(-dpad_distance, 0), Direction::Left, dpad_arrow_size); 685 DrawArrow(p, dpad_center + QPointF(-dpad_distance, 0), Direction::Left, dpad_arrow_size);
684 686
685 // ZL and ZR buttons 687 // ZL and ZR buttons
686 p.setPen(colors.outline); 688 p.setPen(colors.outline);
diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_network.cpp
index 4aa424803..ae22f1018 100644
--- a/src/yuzu/configuration/configure_service.cpp
+++ b/src/yuzu/configuration/configure_network.cpp
@@ -5,9 +5,11 @@
5#include <QGraphicsItem> 5#include <QGraphicsItem>
6#include <QtConcurrent/QtConcurrent> 6#include <QtConcurrent/QtConcurrent>
7#include "common/settings.h" 7#include "common/settings.h"
8#include "core/core.h"
8#include "core/hle/service/bcat/backend/boxcat.h" 9#include "core/hle/service/bcat/backend/boxcat.h"
9#include "ui_configure_service.h" 10#include "core/network/network_interface.h"
10#include "yuzu/configuration/configure_service.h" 11#include "ui_configure_network.h"
12#include "yuzu/configuration/configure_network.h"
11 13
12#ifdef YUZU_ENABLE_BOXCAT 14#ifdef YUZU_ENABLE_BOXCAT
13namespace { 15namespace {
@@ -35,8 +37,8 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) {
35} // Anonymous namespace 37} // Anonymous namespace
36#endif 38#endif
37 39
38ConfigureService::ConfigureService(QWidget* parent) 40ConfigureNetwork::ConfigureNetwork(QWidget* parent)
39 : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) { 41 : QWidget(parent), ui(std::make_unique<Ui::ConfigureNetwork>()) {
40 ui->setupUi(this); 42 ui->setupUi(this);
41 43
42 ui->bcat_source->addItem(QStringLiteral("None")); 44 ui->bcat_source->addItem(QStringLiteral("None"));
@@ -47,29 +49,42 @@ ConfigureService::ConfigureService(QWidget* parent)
47 ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat")); 49 ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat"));
48#endif 50#endif
49 51
52 ui->network_interface->addItem(tr("None"));
53 for (const auto& iface : Network::GetAvailableNetworkInterfaces()) {
54 ui->network_interface->addItem(QString::fromStdString(iface.name));
55 }
56
50 connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this, 57 connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
51 &ConfigureService::OnBCATImplChanged); 58 &ConfigureNetwork::OnBCATImplChanged);
52 59
53 this->SetConfiguration(); 60 this->SetConfiguration();
54} 61}
55 62
56ConfigureService::~ConfigureService() = default; 63ConfigureNetwork::~ConfigureNetwork() = default;
57 64
58void ConfigureService::ApplyConfiguration() { 65void ConfigureNetwork::ApplyConfiguration() {
59 Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString(); 66 Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString();
67 Settings::values.network_interface = ui->network_interface->currentText().toStdString();
60} 68}
61 69
62void ConfigureService::RetranslateUi() { 70void ConfigureNetwork::RetranslateUi() {
63 ui->retranslateUi(this); 71 ui->retranslateUi(this);
64} 72}
65 73
66void ConfigureService::SetConfiguration() { 74void ConfigureNetwork::SetConfiguration() {
75 const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
76
67 const int index = 77 const int index =
68 ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue())); 78 ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue()));
69 ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index); 79 ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index);
80
81 const std::string& network_interface = Settings::values.network_interface.GetValue();
82
83 ui->network_interface->setCurrentText(QString::fromStdString(network_interface));
84 ui->network_interface->setEnabled(runtime_lock);
70} 85}
71 86
72std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { 87std::pair<QString, QString> ConfigureNetwork::BCATDownloadEvents() {
73#ifdef YUZU_ENABLE_BOXCAT 88#ifdef YUZU_ENABLE_BOXCAT
74 std::optional<std::string> global; 89 std::optional<std::string> global;
75 std::map<std::string, Service::BCAT::EventStatus> map; 90 std::map<std::string, Service::BCAT::EventStatus> map;
@@ -114,7 +129,7 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
114#endif 129#endif
115} 130}
116 131
117void ConfigureService::OnBCATImplChanged() { 132void ConfigureNetwork::OnBCATImplChanged() {
118#ifdef YUZU_ENABLE_BOXCAT 133#ifdef YUZU_ENABLE_BOXCAT
119 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); 134 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
120 ui->bcat_empty_header->setHidden(!boxcat); 135 ui->bcat_empty_header->setHidden(!boxcat);
@@ -133,7 +148,7 @@ void ConfigureService::OnBCATImplChanged() {
133#endif 148#endif
134} 149}
135 150
136void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { 151void ConfigureNetwork::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) {
137#ifdef YUZU_ENABLE_BOXCAT 152#ifdef YUZU_ENABLE_BOXCAT
138 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat"); 153 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
139 if (boxcat) { 154 if (boxcat) {
diff --git a/src/yuzu/configuration/configure_service.h b/src/yuzu/configuration/configure_network.h
index f5c1b703a..442b68e6b 100644
--- a/src/yuzu/configuration/configure_service.h
+++ b/src/yuzu/configuration/configure_network.h
@@ -9,15 +9,15 @@
9#include <QWidget> 9#include <QWidget>
10 10
11namespace Ui { 11namespace Ui {
12class ConfigureService; 12class ConfigureNetwork;
13} 13}
14 14
15class ConfigureService : public QWidget { 15class ConfigureNetwork : public QWidget {
16 Q_OBJECT 16 Q_OBJECT
17 17
18public: 18public:
19 explicit ConfigureService(QWidget* parent = nullptr); 19 explicit ConfigureNetwork(QWidget* parent = nullptr);
20 ~ConfigureService() override; 20 ~ConfigureNetwork() override;
21 21
22 void ApplyConfiguration(); 22 void ApplyConfiguration();
23 void RetranslateUi(); 23 void RetranslateUi();
@@ -29,6 +29,6 @@ private:
29 void OnBCATImplChanged(); 29 void OnBCATImplChanged();
30 void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string); 30 void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string);
31 31
32 std::unique_ptr<Ui::ConfigureService> ui; 32 std::unique_ptr<Ui::ConfigureNetwork> ui;
33 QFutureWatcher<std::pair<QString, QString>> watcher{this}; 33 QFutureWatcher<std::pair<QString, QString>> watcher{this};
34}; 34};
diff --git a/src/yuzu/configuration/configure_service.ui b/src/yuzu/configuration/configure_network.ui
index 9668dd557..5f9b7e97b 100644
--- a/src/yuzu/configuration/configure_service.ui
+++ b/src/yuzu/configuration/configure_network.ui
@@ -1,7 +1,7 @@
1<?xml version="1.0" encoding="UTF-8"?> 1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0"> 2<ui version="4.0">
3 <class>ConfigureService</class> 3 <class>ConfigureNetwork</class>
4 <widget class="QWidget" name="ConfigureService"> 4 <widget class="QWidget" name="ConfigureNetwork">
5 <property name="geometry"> 5 <property name="geometry">
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
@@ -17,21 +17,37 @@
17 <item> 17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3"> 18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item> 19 <item>
20 <widget class="QGroupBox" name="groupBox_2">
21 <property name="title">
22 <string>General</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout_2">
25 <item row="1" column="1">
26 <widget class="QComboBox" name="network_interface"/>
27 </item>
28 <item row="1" column="0">
29 <widget class="QLabel" name="label_4">
30 <property name="text">
31 <string>Network Interface</string>
32 </property>
33 </widget>
34 </item>
35 </layout>
36 </widget>
37 </item>
38 <item>
20 <widget class="QGroupBox" name="groupBox"> 39 <widget class="QGroupBox" name="groupBox">
21 <property name="title"> 40 <property name="title">
22 <string>BCAT</string> 41 <string>BCAT</string>
23 </property> 42 </property>
24 <layout class="QGridLayout" name="gridLayout"> 43 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="1" colspan="2"> 44 <item row="3" column="0">
26 <widget class="QLabel" name="label_2"> 45 <widget class="QLabel" name="bcat_empty_header">
27 <property name="maximumSize">
28 <size>
29 <width>260</width>
30 <height>16777215</height>
31 </size>
32 </property>
33 <property name="text"> 46 <property name="text">
34 <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string> 47 <string/>
48 </property>
49 <property name="alignment">
50 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
35 </property> 51 </property>
36 <property name="wordWrap"> 52 <property name="wordWrap">
37 <bool>true</bool> 53 <bool>true</bool>
@@ -51,11 +67,8 @@
51 </property> 67 </property>
52 </widget> 68 </widget>
53 </item> 69 </item>
54 <item row="3" column="1" colspan="2"> 70 <item row="1" column="1" colspan="2">
55 <widget class="QLabel" name="bcat_empty_label"> 71 <widget class="QLabel" name="label_2">
56 <property name="enabled">
57 <bool>true</bool>
58 </property>
59 <property name="maximumSize"> 72 <property name="maximumSize">
60 <size> 73 <size>
61 <width>260</width> 74 <width>260</width>
@@ -63,10 +76,7 @@
63 </size> 76 </size>
64 </property> 77 </property>
65 <property name="text"> 78 <property name="text">
66 <string/> 79 <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string>
67 </property>
68 <property name="alignment">
69 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
70 </property> 80 </property>
71 <property name="wordWrap"> 81 <property name="wordWrap">
72 <bool>true</bool> 82 <bool>true</bool>
@@ -86,8 +96,17 @@
86 <item row="0" column="1" colspan="2"> 96 <item row="0" column="1" colspan="2">
87 <widget class="QComboBox" name="bcat_source"/> 97 <widget class="QComboBox" name="bcat_source"/>
88 </item> 98 </item>
89 <item row="3" column="0"> 99 <item row="3" column="1" colspan="2">
90 <widget class="QLabel" name="bcat_empty_header"> 100 <widget class="QLabel" name="bcat_empty_label">
101 <property name="enabled">
102 <bool>true</bool>
103 </property>
104 <property name="maximumSize">
105 <size>
106 <width>260</width>
107 <height>16777215</height>
108 </size>
109 </property>
91 <property name="text"> 110 <property name="text">
92 <string/> 111 <string/>
93 </property> 112 </property>
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp
index 22ca1285d..f89ea8ea7 100644
--- a/src/yuzu/debugger/console.cpp
+++ b/src/yuzu/debugger/console.cpp
@@ -21,6 +21,7 @@ void ToggleConsole() {
21 console_shown = UISettings::values.show_console.GetValue(); 21 console_shown = UISettings::values.show_console.GetValue();
22 } 22 }
23 23
24 using namespace Common::Log;
24#if defined(_WIN32) && !defined(_DEBUG) 25#if defined(_WIN32) && !defined(_DEBUG)
25 FILE* temp; 26 FILE* temp;
26 if (UISettings::values.show_console) { 27 if (UISettings::values.show_console) {
@@ -29,24 +30,20 @@ void ToggleConsole() {
29 freopen_s(&temp, "CONIN$", "r", stdin); 30 freopen_s(&temp, "CONIN$", "r", stdin);
30 freopen_s(&temp, "CONOUT$", "w", stdout); 31 freopen_s(&temp, "CONOUT$", "w", stdout);
31 freopen_s(&temp, "CONOUT$", "w", stderr); 32 freopen_s(&temp, "CONOUT$", "w", stderr);
32 Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>()); 33 SetColorConsoleBackendEnabled(true);
33 } 34 }
34 } else { 35 } else {
35 if (FreeConsole()) { 36 if (FreeConsole()) {
36 // In order to close the console, we have to also detach the streams on it. 37 // In order to close the console, we have to also detach the streams on it.
37 // Just redirect them to NUL if there is no console window 38 // Just redirect them to NUL if there is no console window
38 Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name()); 39 SetColorConsoleBackendEnabled(false);
39 freopen_s(&temp, "NUL", "r", stdin); 40 freopen_s(&temp, "NUL", "r", stdin);
40 freopen_s(&temp, "NUL", "w", stdout); 41 freopen_s(&temp, "NUL", "w", stdout);
41 freopen_s(&temp, "NUL", "w", stderr); 42 freopen_s(&temp, "NUL", "w", stderr);
42 } 43 }
43 } 44 }
44#else 45#else
45 if (UISettings::values.show_console) { 46 SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue());
46 Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>());
47 } else {
48 Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name());
49 }
50#endif 47#endif
51} 48}
52} // namespace Debugger 49} // namespace Debugger
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 5940e0cfd..e36774cc6 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -175,21 +175,6 @@ void GMainWindow::ShowTelemetryCallout() {
175 175
176const int GMainWindow::max_recent_files_item; 176const int GMainWindow::max_recent_files_item;
177 177
178static void InitializeLogging() {
179 using namespace Common;
180
181 Log::Filter log_filter;
182 log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
183 Log::SetGlobalFilter(log_filter);
184
185 const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
186 void(FS::CreateDir(log_dir));
187 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
188#ifdef _WIN32
189 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
190#endif
191}
192
193static void RemoveCachedContents() { 178static void RemoveCachedContents() {
194 const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); 179 const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir);
195 const auto offline_fonts = cache_dir / "fonts"; 180 const auto offline_fonts = cache_dir / "fonts";
@@ -207,8 +192,7 @@ GMainWindow::GMainWindow()
207 : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, 192 : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
208 config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()}, 193 config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
209 provider{std::make_unique<FileSys::ManualContentProvider>()} { 194 provider{std::make_unique<FileSys::ManualContentProvider>()} {
210 InitializeLogging(); 195 Common::Log::Initialize();
211
212 LoadTranslation(); 196 LoadTranslation();
213 197
214 setAcceptDrops(true); 198 setAcceptDrops(true);
@@ -3437,6 +3421,7 @@ int main(int argc, char* argv[]) {
3437 // generating shaders 3421 // generating shaders
3438 setlocale(LC_ALL, "C"); 3422 setlocale(LC_ALL, "C");
3439 3423
3424 Core::System::InitializeGlobalInstance();
3440 GMainWindow main_window; 3425 GMainWindow main_window;
3441 // After settings have been loaded by GMainWindow, apply the filter 3426 // After settings have been loaded by GMainWindow, apply the filter
3442 main_window.show(); 3427 main_window.show();
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c10093820..ba2c993ba 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -74,31 +74,14 @@ static void PrintVersion() {
74 std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; 74 std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
75} 75}
76 76
77static void InitializeLogging() {
78 using namespace Common;
79
80 Log::Filter log_filter(Log::Level::Debug);
81 log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter));
82 Log::SetGlobalFilter(log_filter);
83
84 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
85
86 const auto& log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
87 void(FS::CreateDir(log_dir));
88 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
89#ifdef _WIN32
90 Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
91#endif
92}
93
94/// Application entry point 77/// Application entry point
95int main(int argc, char** argv) { 78int main(int argc, char** argv) {
79 Common::Log::Initialize();
80 Common::Log::SetColorConsoleBackendEnabled(true);
96 Common::DetachedTasks detached_tasks; 81 Common::DetachedTasks detached_tasks;
97 Config config; 82 Config config;
98 83
99 int option_index = 0; 84 int option_index = 0;
100
101 InitializeLogging();
102#ifdef _WIN32 85#ifdef _WIN32
103 int argc_w; 86 int argc_w;
104 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w); 87 auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
@@ -163,6 +146,7 @@ int main(int argc, char** argv) {
163 return -1; 146 return -1;
164 } 147 }
165 148
149 Core::System::InitializeGlobalInstance();
166 auto& system{Core::System::GetInstance()}; 150 auto& system{Core::System::GetInstance()};
167 InputCommon::InputSubsystem input_subsystem; 151 InputCommon::InputSubsystem input_subsystem;
168 152