diff options
Diffstat (limited to 'src')
53 files changed, 936 insertions, 571 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 61dddab3f..13edda9c9 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -2,13 +2,9 @@ | |||
| 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> | ||
| 10 | #include <memory> | ||
| 11 | #include <mutex> | ||
| 12 | #include <thread> | 8 | #include <thread> |
| 13 | #include <vector> | 9 | #include <vector> |
| 14 | 10 | ||
| @@ -16,104 +12,229 @@ | |||
| 16 | #include <windows.h> // For OutputDebugStringW | 12 | #include <windows.h> // For OutputDebugStringW |
| 17 | #endif | 13 | #endif |
| 18 | 14 | ||
| 19 | #include "common/assert.h" | ||
| 20 | #include "common/fs/file.h" | 15 | #include "common/fs/file.h" |
| 21 | #include "common/fs/fs.h" | 16 | #include "common/fs/fs.h" |
| 17 | #include "common/fs/fs_paths.h" | ||
| 18 | #include "common/fs/path_util.h" | ||
| 22 | #include "common/literals.h" | 19 | #include "common/literals.h" |
| 23 | 20 | ||
| 24 | #include "common/logging/backend.h" | 21 | #include "common/logging/backend.h" |
| 25 | #include "common/logging/log.h" | 22 | #include "common/logging/log.h" |
| 26 | #include "common/logging/text_formatter.h" | 23 | #include "common/logging/text_formatter.h" |
| 27 | #include "common/settings.h" | 24 | #include "common/settings.h" |
| 25 | #ifdef _WIN32 | ||
| 28 | #include "common/string_util.h" | 26 | #include "common/string_util.h" |
| 27 | #endif | ||
| 29 | #include "common/threadsafe_queue.h" | 28 | #include "common/threadsafe_queue.h" |
| 30 | 29 | ||
| 31 | namespace Common::Log { | 30 | namespace Common::Log { |
| 32 | 31 | ||
| 32 | namespace { | ||
| 33 | |||
| 33 | /** | 34 | /** |
| 34 | * Static state as a singleton. | 35 | * Interface for logging backends. |
| 35 | */ | 36 | */ |
| 36 | class Impl { | 37 | class Backend { |
| 37 | public: | 38 | public: |
| 38 | static Impl& Instance() { | 39 | virtual ~Backend() = default; |
| 39 | static Impl backend; | 40 | |
| 40 | return backend; | 41 | virtual void Write(const Entry& entry) = 0; |
| 42 | |||
| 43 | virtual void EnableForStacktrace() = 0; | ||
| 44 | |||
| 45 | virtual void Flush() = 0; | ||
| 46 | }; | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Backend that writes to stderr and with color | ||
| 50 | */ | ||
| 51 | class ColorConsoleBackend final : public Backend { | ||
| 52 | public: | ||
| 53 | explicit ColorConsoleBackend() = default; | ||
| 54 | |||
| 55 | ~ColorConsoleBackend() override = default; | ||
| 56 | |||
| 57 | void Write(const Entry& entry) override { | ||
| 58 | if (enabled.load(std::memory_order_relaxed)) { | ||
| 59 | PrintColoredMessage(entry); | ||
| 60 | } | ||
| 41 | } | 61 | } |
| 42 | 62 | ||
| 43 | Impl(const Impl&) = delete; | 63 | void Flush() override { |
| 44 | Impl& operator=(const Impl&) = delete; | 64 | // stderr shouldn't be buffered |
| 65 | } | ||
| 45 | 66 | ||
| 46 | Impl(Impl&&) = delete; | 67 | void EnableForStacktrace() override { |
| 47 | Impl& operator=(Impl&&) = delete; | 68 | enabled = true; |
| 69 | } | ||
| 48 | 70 | ||
| 49 | void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, | 71 | void SetEnabled(bool enabled_) { |
| 50 | const char* function, std::string message) { | 72 | enabled = enabled_; |
| 51 | message_queue.Push( | 73 | } |
| 52 | CreateEntry(log_class, log_level, filename, line_num, function, std::move(message))); | 74 | |
| 75 | private: | ||
| 76 | std::atomic_bool enabled{false}; | ||
| 77 | }; | ||
| 78 | |||
| 79 | /** | ||
| 80 | * Backend that writes to a file passed into the constructor | ||
| 81 | */ | ||
| 82 | class FileBackend final : public Backend { | ||
| 83 | public: | ||
| 84 | explicit FileBackend(const std::filesystem::path& filename) { | ||
| 85 | auto old_filename = filename; | ||
| 86 | old_filename += ".old.txt"; | ||
| 87 | |||
| 88 | // Existence checks are done within the functions themselves. | ||
| 89 | // We don't particularly care if these succeed or not. | ||
| 90 | static_cast<void>(FS::RemoveFile(old_filename)); | ||
| 91 | static_cast<void>(FS::RenameFile(filename, old_filename)); | ||
| 92 | |||
| 93 | file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, | ||
| 94 | FS::FileType::TextFile); | ||
| 95 | } | ||
| 96 | |||
| 97 | ~FileBackend() override = default; | ||
| 98 | |||
| 99 | void Write(const Entry& entry) override { | ||
| 100 | if (!enabled) { | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | |||
| 104 | bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); | ||
| 105 | |||
| 106 | using namespace Common::Literals; | ||
| 107 | // Prevent logs from exceeding a set maximum size in the event that log entries are spammed. | ||
| 108 | const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB; | ||
| 109 | const bool write_limit_exceeded = bytes_written > write_limit; | ||
| 110 | if (entry.log_level >= Level::Error || write_limit_exceeded) { | ||
| 111 | if (write_limit_exceeded) { | ||
| 112 | // Stop writing after the write limit is exceeded. | ||
| 113 | // Don't close the file so we can print a stacktrace if necessary | ||
| 114 | enabled = false; | ||
| 115 | } | ||
| 116 | file->Flush(); | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | void Flush() override { | ||
| 121 | file->Flush(); | ||
| 122 | } | ||
| 123 | |||
| 124 | void EnableForStacktrace() override { | ||
| 125 | enabled = true; | ||
| 126 | bytes_written = 0; | ||
| 53 | } | 127 | } |
| 54 | 128 | ||
| 55 | void AddBackend(std::unique_ptr<Backend> backend) { | 129 | private: |
| 56 | std::lock_guard lock{writing_mutex}; | 130 | std::unique_ptr<FS::IOFile> file; |
| 57 | backends.push_back(std::move(backend)); | 131 | bool enabled = true; |
| 132 | std::size_t bytes_written = 0; | ||
| 133 | }; | ||
| 134 | |||
| 135 | /** | ||
| 136 | * Backend that writes to Visual Studio's output window | ||
| 137 | */ | ||
| 138 | class DebuggerBackend final : public Backend { | ||
| 139 | public: | ||
| 140 | explicit DebuggerBackend() = default; | ||
| 141 | |||
| 142 | ~DebuggerBackend() override = default; | ||
| 143 | |||
| 144 | void Write(const Entry& entry) override { | ||
| 145 | #ifdef _WIN32 | ||
| 146 | ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); | ||
| 147 | #endif | ||
| 58 | } | 148 | } |
| 59 | 149 | ||
| 60 | void RemoveBackend(std::string_view backend_name) { | 150 | void Flush() override {} |
| 61 | std::lock_guard lock{writing_mutex}; | 151 | |
| 152 | void EnableForStacktrace() override {} | ||
| 153 | }; | ||
| 154 | |||
| 155 | bool initialization_in_progress_suppress_logging = false; | ||
| 62 | 156 | ||
| 63 | std::erase_if(backends, [&backend_name](const auto& backend) { | 157 | /** |
| 64 | return backend_name == backend->GetName(); | 158 | * Static state as a singleton. |
| 65 | }); | 159 | */ |
| 160 | class Impl { | ||
| 161 | public: | ||
| 162 | static Impl& Instance() { | ||
| 163 | if (!instance) { | ||
| 164 | abort(); | ||
| 165 | } | ||
| 166 | return *instance; | ||
| 66 | } | 167 | } |
| 67 | 168 | ||
| 68 | const Filter& GetGlobalFilter() const { | 169 | static void Initialize() { |
| 69 | return filter; | 170 | if (instance) { |
| 171 | abort(); | ||
| 172 | } | ||
| 173 | using namespace Common::FS; | ||
| 174 | initialization_in_progress_suppress_logging = true; | ||
| 175 | const auto& log_dir = GetYuzuPath(YuzuPath::LogDir); | ||
| 176 | void(CreateDir(log_dir)); | ||
| 177 | Filter filter; | ||
| 178 | filter.ParseFilterString(Settings::values.log_filter.GetValue()); | ||
| 179 | instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter), | ||
| 180 | Deleter); | ||
| 181 | initialization_in_progress_suppress_logging = false; | ||
| 70 | } | 182 | } |
| 71 | 183 | ||
| 184 | Impl(const Impl&) = delete; | ||
| 185 | Impl& operator=(const Impl&) = delete; | ||
| 186 | |||
| 187 | Impl(Impl&&) = delete; | ||
| 188 | Impl& operator=(Impl&&) = delete; | ||
| 189 | |||
| 72 | void SetGlobalFilter(const Filter& f) { | 190 | void SetGlobalFilter(const Filter& f) { |
| 73 | filter = f; | 191 | filter = f; |
| 74 | } | 192 | } |
| 75 | 193 | ||
| 76 | Backend* GetBackend(std::string_view backend_name) { | 194 | void SetColorConsoleBackendEnabled(bool enabled) { |
| 77 | const auto it = | 195 | color_console_backend.SetEnabled(enabled); |
| 78 | std::find_if(backends.begin(), backends.end(), | 196 | } |
| 79 | [&backend_name](const auto& i) { return backend_name == i->GetName(); }); | 197 | |
| 80 | if (it == backends.end()) | 198 | void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num, |
| 81 | return nullptr; | 199 | const char* function, std::string message) { |
| 82 | return it->get(); | 200 | if (!filter.CheckMessage(log_class, log_level)) |
| 201 | return; | ||
| 202 | const Entry& entry = | ||
| 203 | CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)); | ||
| 204 | message_queue.Push(entry); | ||
| 83 | } | 205 | } |
| 84 | 206 | ||
| 85 | private: | 207 | private: |
| 86 | Impl() { | 208 | Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_) |
| 87 | backend_thread = std::thread([&] { | 209 | : filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] { |
| 88 | Entry entry; | 210 | Common::SetCurrentThreadName("yuzu:Log"); |
| 89 | auto write_logs = [&](Entry& e) { | 211 | Entry entry; |
| 90 | std::lock_guard lock{writing_mutex}; | 212 | const auto write_logs = [this, &entry]() { |
| 91 | for (const auto& backend : backends) { | 213 | ForEachBackend([&entry](Backend& backend) { backend.Write(entry); }); |
| 92 | backend->Write(e); | 214 | }; |
| 93 | } | 215 | while (true) { |
| 94 | }; | 216 | entry = message_queue.PopWait(); |
| 95 | while (true) { | 217 | if (entry.final_entry) { |
| 96 | entry = message_queue.PopWait(); | 218 | break; |
| 97 | if (entry.final_entry) { | 219 | } |
| 98 | break; | 220 | write_logs(); |
| 99 | } | 221 | } |
| 100 | write_logs(entry); | 222 | // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a |
| 101 | } | 223 | // case where a system is repeatedly spamming logs even on close. |
| 224 | int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100; | ||
| 225 | while (max_logs_to_write-- && message_queue.Pop(entry)) { | ||
| 226 | write_logs(); | ||
| 227 | } | ||
| 228 | })} {} | ||
| 102 | 229 | ||
| 103 | // Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a | 230 | ~Impl() { |
| 104 | // case where a system is repeatedly spamming logs even on close. | 231 | 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 | } | 232 | } |
| 112 | 233 | ||
| 113 | ~Impl() { | 234 | void StopBackendThread() { |
| 114 | Entry entry; | 235 | Entry stop_entry{}; |
| 115 | entry.final_entry = true; | 236 | stop_entry.final_entry = true; |
| 116 | message_queue.Push(entry); | 237 | message_queue.Push(stop_entry); |
| 117 | backend_thread.join(); | 238 | backend_thread.join(); |
| 118 | } | 239 | } |
| 119 | 240 | ||
| @@ -135,100 +256,51 @@ private: | |||
| 135 | }; | 256 | }; |
| 136 | } | 257 | } |
| 137 | 258 | ||
| 138 | std::mutex writing_mutex; | 259 | void ForEachBackend(auto lambda) { |
| 139 | std::thread backend_thread; | 260 | lambda(static_cast<Backend&>(debugger_backend)); |
| 140 | std::vector<std::unique_ptr<Backend>> backends; | 261 | lambda(static_cast<Backend&>(color_console_backend)); |
| 141 | MPSCQueue<Entry> message_queue; | 262 | lambda(static_cast<Backend&>(file_backend)); |
| 142 | Filter filter; | 263 | } |
| 143 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; | ||
| 144 | }; | ||
| 145 | |||
| 146 | ConsoleBackend::~ConsoleBackend() = default; | ||
| 147 | |||
| 148 | void ConsoleBackend::Write(const Entry& entry) { | ||
| 149 | PrintMessage(entry); | ||
| 150 | } | ||
| 151 | |||
| 152 | ColorConsoleBackend::~ColorConsoleBackend() = default; | ||
| 153 | |||
| 154 | void ColorConsoleBackend::Write(const Entry& entry) { | ||
| 155 | PrintColoredMessage(entry); | ||
| 156 | } | ||
| 157 | |||
| 158 | FileBackend::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 | |||
| 171 | FileBackend::~FileBackend() = default; | ||
| 172 | 264 | ||
| 173 | void FileBackend::Write(const Entry& entry) { | 265 | static void Deleter(Impl* ptr) { |
| 174 | if (!file->IsOpen()) { | 266 | delete ptr; |
| 175 | return; | ||
| 176 | } | 267 | } |
| 177 | 268 | ||
| 178 | using namespace Common::Literals; | 269 | 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 | 270 | ||
| 183 | const bool write_limit_exceeded = | 271 | Filter filter; |
| 184 | bytes_written > MAX_BYTES_WRITTEN_EXTENDED || | 272 | DebuggerBackend debugger_backend{}; |
| 185 | (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging); | 273 | ColorConsoleBackend color_console_backend{}; |
| 274 | FileBackend file_backend; | ||
| 186 | 275 | ||
| 187 | // Close the file after the write limit is exceeded. | 276 | std::thread backend_thread; |
| 188 | if (write_limit_exceeded) { | 277 | MPSCQueue<Entry> message_queue{}; |
| 189 | file->Close(); | 278 | std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()}; |
| 190 | return; | 279 | }; |
| 191 | } | 280 | } // namespace |
| 192 | 281 | ||
| 193 | bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n')); | 282 | void Initialize() { |
| 194 | if (entry.log_level >= Level::Error) { | 283 | Impl::Initialize(); |
| 195 | file->Flush(); | ||
| 196 | } | ||
| 197 | } | 284 | } |
| 198 | 285 | ||
| 199 | DebuggerBackend::~DebuggerBackend() = default; | 286 | void DisableLoggingInTests() { |
| 200 | 287 | initialization_in_progress_suppress_logging = true; | |
| 201 | void DebuggerBackend::Write(const Entry& entry) { | ||
| 202 | #ifdef _WIN32 | ||
| 203 | ::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); | ||
| 204 | #endif | ||
| 205 | } | 288 | } |
| 206 | 289 | ||
| 207 | void SetGlobalFilter(const Filter& filter) { | 290 | void SetGlobalFilter(const Filter& filter) { |
| 208 | Impl::Instance().SetGlobalFilter(filter); | 291 | Impl::Instance().SetGlobalFilter(filter); |
| 209 | } | 292 | } |
| 210 | 293 | ||
| 211 | void AddBackend(std::unique_ptr<Backend> backend) { | 294 | void SetColorConsoleBackendEnabled(bool enabled) { |
| 212 | Impl::Instance().AddBackend(std::move(backend)); | 295 | Impl::Instance().SetColorConsoleBackendEnabled(enabled); |
| 213 | } | ||
| 214 | |||
| 215 | void RemoveBackend(std::string_view backend_name) { | ||
| 216 | Impl::Instance().RemoveBackend(backend_name); | ||
| 217 | } | ||
| 218 | |||
| 219 | Backend* GetBackend(std::string_view backend_name) { | ||
| 220 | return Impl::Instance().GetBackend(backend_name); | ||
| 221 | } | 296 | } |
| 222 | 297 | ||
| 223 | void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, | 298 | void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, |
| 224 | unsigned int line_num, const char* function, const char* format, | 299 | unsigned int line_num, const char* function, const char* format, |
| 225 | const fmt::format_args& args) { | 300 | const fmt::format_args& args) { |
| 226 | auto& instance = Impl::Instance(); | 301 | if (!initialization_in_progress_suppress_logging) { |
| 227 | const auto& filter = instance.GetGlobalFilter(); | 302 | Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, |
| 228 | if (!filter.CheckMessage(log_class, log_level)) | 303 | fmt::vformat(format, args)); |
| 229 | return; | 304 | } |
| 230 | |||
| 231 | instance.PushEntry(log_class, log_level, filename, line_num, function, | ||
| 232 | fmt::vformat(format, args)); | ||
| 233 | } | 305 | } |
| 234 | } // namespace Common::Log | 306 | } // 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 | |||
| 14 | namespace Common::FS { | ||
| 15 | class IOFile; | ||
| 16 | } | ||
| 17 | 9 | ||
| 18 | namespace Common::Log { | 10 | namespace Common::Log { |
| 19 | 11 | ||
| 20 | class Filter; | 12 | class 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 | 15 | void Initialize(); |
| 24 | * used by a frontend for adding a custom logging backend as needed | ||
| 25 | */ | ||
| 26 | class Backend { | ||
| 27 | public: | ||
| 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 | |||
| 36 | private: | ||
| 37 | Filter filter; | ||
| 38 | }; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * Backend that writes to stderr without any color commands | ||
| 42 | */ | ||
| 43 | class ConsoleBackend : public Backend { | ||
| 44 | public: | ||
| 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 | */ | ||
| 59 | class ColorConsoleBackend : public Backend { | ||
| 60 | public: | ||
| 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 | /** | 17 | void DisableLoggingInTests(); |
| 74 | * Backend that writes to a file passed into the constructor | ||
| 75 | */ | ||
| 76 | class FileBackend : public Backend { | ||
| 77 | public: | ||
| 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 | |||
| 91 | private: | ||
| 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 | */ | ||
| 99 | class DebuggerBackend : public Backend { | ||
| 100 | public: | ||
| 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 | |||
| 112 | void AddBackend(std::unique_ptr<Backend> backend); | ||
| 113 | |||
| 114 | void RemoveBackend(std::string_view backend_name); | ||
| 115 | |||
| 116 | Backend* 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 | */ |
| 123 | void SetGlobalFilter(const Filter& filter); | 22 | void SetGlobalFilter(const Filter& filter); |
| 124 | } // namespace Common::Log \ No newline at end of file | 23 | |
| 24 | void SetColorConsoleBackendEnabled(bool enabled); | ||
| 25 | } // namespace Common::Log | ||
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 | ||
| 12 | namespace Common::X64 { | 12 | namespace 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 | ||
| 11 | namespace Common::X64 { | 11 | namespace Common::X64 { |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5c99c00f5..f5cf5c16a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -636,6 +636,8 @@ add_library(core STATIC | |||
| 636 | memory.h | 636 | memory.h |
| 637 | network/network.cpp | 637 | network/network.cpp |
| 638 | network/network.h | 638 | network/network.h |
| 639 | network/network_interface.cpp | ||
| 640 | network/network_interface.h | ||
| 639 | network/sockets.h | 641 | network/sockets.h |
| 640 | perf_stats.cpp | 642 | perf_stats.cpp |
| 641 | perf_stats.h | 643 | perf_stats.h |
diff --git a/src/core/core.cpp b/src/core/core.cpp index d3e84c4ef..b0dc594d4 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -84,8 +84,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot( | |||
| 84 | 84 | ||
| 85 | } // Anonymous namespace | 85 | } // Anonymous namespace |
| 86 | 86 | ||
| 87 | /*static*/ System System::s_instance; | ||
| 88 | |||
| 89 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | 87 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, |
| 90 | const std::string& path) { | 88 | const std::string& path) { |
| 91 | // To account for split 00+01+etc files. | 89 | // To account for split 00+01+etc files. |
| @@ -425,6 +423,13 @@ struct System::Impl { | |||
| 425 | System::System() : impl{std::make_unique<Impl>(*this)} {} | 423 | System::System() : impl{std::make_unique<Impl>(*this)} {} |
| 426 | System::~System() = default; | 424 | System::~System() = default; |
| 427 | 425 | ||
| 426 | void System::InitializeGlobalInstance() { | ||
| 427 | if (s_instance) { | ||
| 428 | abort(); | ||
| 429 | } | ||
| 430 | s_instance = std::unique_ptr<System>(new System); | ||
| 431 | } | ||
| 432 | |||
| 428 | CpuManager& System::GetCpuManager() { | 433 | CpuManager& System::GetCpuManager() { |
| 429 | return impl->cpu_manager; | 434 | return impl->cpu_manager; |
| 430 | } | 435 | } |
| @@ -494,12 +499,6 @@ const ARM_Interface& System::CurrentArmInterface() const { | |||
| 494 | return impl->kernel.CurrentPhysicalCore().ArmInterface(); | 499 | return impl->kernel.CurrentPhysicalCore().ArmInterface(); |
| 495 | } | 500 | } |
| 496 | 501 | ||
| 497 | std::size_t System::CurrentCoreIndex() const { | ||
| 498 | std::size_t core = impl->kernel.GetCurrentHostThreadID(); | ||
| 499 | ASSERT(core < Core::Hardware::NUM_CPU_CORES); | ||
| 500 | return core; | ||
| 501 | } | ||
| 502 | |||
| 503 | Kernel::PhysicalCore& System::CurrentPhysicalCore() { | 502 | Kernel::PhysicalCore& System::CurrentPhysicalCore() { |
| 504 | return impl->kernel.CurrentPhysicalCore(); | 503 | return impl->kernel.CurrentPhysicalCore(); |
| 505 | } | 504 | } |
diff --git a/src/core/core.h b/src/core/core.h index ea143043c..65b447a1c 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -121,9 +121,14 @@ public: | |||
| 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 | if (!s_instance) { |
| 125 | abort(); | ||
| 126 | } | ||
| 127 | return *s_instance; | ||
| 125 | } | 128 | } |
| 126 | 129 | ||
| 130 | static void InitializeGlobalInstance(); | ||
| 131 | |||
| 127 | /// Enumeration representing the return values of the System Initialize and Load process. | 132 | /// Enumeration representing the return values of the System Initialize and Load process. |
| 128 | enum class ResultStatus : u32 { | 133 | enum class ResultStatus : u32 { |
| 129 | Success, ///< Succeeded | 134 | Success, ///< Succeeded |
| @@ -205,9 +210,6 @@ public: | |||
| 205 | /// Gets an ARM interface to the CPU core that is currently running | 210 | /// Gets an ARM interface to the CPU core that is currently running |
| 206 | [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; | 211 | [[nodiscard]] const ARM_Interface& CurrentArmInterface() const; |
| 207 | 212 | ||
| 208 | /// Gets the index of the currently running CPU core | ||
| 209 | [[nodiscard]] std::size_t CurrentCoreIndex() const; | ||
| 210 | |||
| 211 | /// Gets the physical core for the CPU core that is currently running | 213 | /// Gets the physical core for the CPU core that is currently running |
| 212 | [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); | 214 | [[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore(); |
| 213 | 215 | ||
| @@ -396,7 +398,7 @@ private: | |||
| 396 | struct Impl; | 398 | struct Impl; |
| 397 | std::unique_ptr<Impl> impl; | 399 | std::unique_ptr<Impl> impl; |
| 398 | 400 | ||
| 399 | static System s_instance; | 401 | inline static std::unique_ptr<System> s_instance{}; |
| 400 | }; | 402 | }; |
| 401 | 403 | ||
| 402 | } // namespace Core | 404 | } // namespace Core |
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 7e195346b..de2e5563e 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp | |||
| @@ -21,34 +21,25 @@ namespace Core { | |||
| 21 | CpuManager::CpuManager(System& system_) : system{system_} {} | 21 | CpuManager::CpuManager(System& system_) : system{system_} {} |
| 22 | CpuManager::~CpuManager() = default; | 22 | CpuManager::~CpuManager() = default; |
| 23 | 23 | ||
| 24 | void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) { | 24 | void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, |
| 25 | cpu_manager.RunThread(core); | 25 | std::size_t core) { |
| 26 | cpu_manager.RunThread(stop_token, core); | ||
| 26 | } | 27 | } |
| 27 | 28 | ||
| 28 | void CpuManager::Initialize() { | 29 | void CpuManager::Initialize() { |
| 29 | running_mode = true; | 30 | running_mode = true; |
| 30 | if (is_multicore) { | 31 | if (is_multicore) { |
| 31 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { | 32 | for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { |
| 32 | core_data[core].host_thread = | 33 | core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core); |
| 33 | std::make_unique<std::thread>(ThreadStart, std::ref(*this), core); | ||
| 34 | } | 34 | } |
| 35 | } else { | 35 | } else { |
| 36 | core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0); | 36 | core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0); |
| 37 | } | 37 | } |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | void CpuManager::Shutdown() { | 40 | void CpuManager::Shutdown() { |
| 41 | running_mode = false; | 41 | running_mode = false; |
| 42 | Pause(false); | 42 | Pause(false); |
| 43 | if (is_multicore) { | ||
| 44 | for (auto& data : core_data) { | ||
| 45 | data.host_thread->join(); | ||
| 46 | data.host_thread.reset(); | ||
| 47 | } | ||
| 48 | } else { | ||
| 49 | core_data[0].host_thread->join(); | ||
| 50 | core_data[0].host_thread.reset(); | ||
| 51 | } | ||
| 52 | } | 43 | } |
| 53 | 44 | ||
| 54 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { | 45 | std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() { |
| @@ -127,17 +118,18 @@ void CpuManager::MultiCoreRunGuestLoop() { | |||
| 127 | physical_core = &kernel.CurrentPhysicalCore(); | 118 | physical_core = &kernel.CurrentPhysicalCore(); |
| 128 | } | 119 | } |
| 129 | system.ExitDynarmicProfile(); | 120 | system.ExitDynarmicProfile(); |
| 130 | physical_core->ArmInterface().ClearExclusiveState(); | 121 | { |
| 131 | kernel.CurrentScheduler()->RescheduleCurrentCore(); | 122 | Kernel::KScopedDisableDispatch dd(kernel); |
| 123 | physical_core->ArmInterface().ClearExclusiveState(); | ||
| 124 | } | ||
| 132 | } | 125 | } |
| 133 | } | 126 | } |
| 134 | 127 | ||
| 135 | void CpuManager::MultiCoreRunIdleThread() { | 128 | void CpuManager::MultiCoreRunIdleThread() { |
| 136 | auto& kernel = system.Kernel(); | 129 | auto& kernel = system.Kernel(); |
| 137 | while (true) { | 130 | while (true) { |
| 138 | auto& physical_core = kernel.CurrentPhysicalCore(); | 131 | Kernel::KScopedDisableDispatch dd(kernel); |
| 139 | physical_core.Idle(); | 132 | kernel.CurrentPhysicalCore().Idle(); |
| 140 | kernel.CurrentScheduler()->RescheduleCurrentCore(); | ||
| 141 | } | 133 | } |
| 142 | } | 134 | } |
| 143 | 135 | ||
| @@ -145,12 +137,12 @@ void CpuManager::MultiCoreRunSuspendThread() { | |||
| 145 | auto& kernel = system.Kernel(); | 137 | auto& kernel = system.Kernel(); |
| 146 | kernel.CurrentScheduler()->OnThreadStart(); | 138 | kernel.CurrentScheduler()->OnThreadStart(); |
| 147 | while (true) { | 139 | while (true) { |
| 148 | auto core = kernel.GetCurrentHostThreadID(); | 140 | auto core = kernel.CurrentPhysicalCoreIndex(); |
| 149 | auto& scheduler = *kernel.CurrentScheduler(); | 141 | auto& scheduler = *kernel.CurrentScheduler(); |
| 150 | Kernel::KThread* current_thread = scheduler.GetCurrentThread(); | 142 | Kernel::KThread* current_thread = scheduler.GetCurrentThread(); |
| 151 | Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); | 143 | Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context); |
| 152 | ASSERT(scheduler.ContextSwitchPending()); | 144 | ASSERT(scheduler.ContextSwitchPending()); |
| 153 | ASSERT(core == kernel.GetCurrentHostThreadID()); | 145 | ASSERT(core == kernel.CurrentPhysicalCoreIndex()); |
| 154 | scheduler.RescheduleCurrentCore(); | 146 | scheduler.RescheduleCurrentCore(); |
| 155 | } | 147 | } |
| 156 | } | 148 | } |
| @@ -317,7 +309,7 @@ void CpuManager::Pause(bool paused) { | |||
| 317 | } | 309 | } |
| 318 | } | 310 | } |
| 319 | 311 | ||
| 320 | void CpuManager::RunThread(std::size_t core) { | 312 | void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) { |
| 321 | /// Initialization | 313 | /// Initialization |
| 322 | system.RegisterCoreThread(core); | 314 | system.RegisterCoreThread(core); |
| 323 | std::string name; | 315 | std::string name; |
| @@ -356,8 +348,8 @@ void CpuManager::RunThread(std::size_t core) { | |||
| 356 | sc_sync_first_use = false; | 348 | sc_sync_first_use = false; |
| 357 | } | 349 | } |
| 358 | 350 | ||
| 359 | // Abort if emulation was killed before the session really starts | 351 | // Emulation was stopped |
| 360 | if (!system.IsPoweredOn()) { | 352 | if (stop_token.stop_requested()) { |
| 361 | return; | 353 | return; |
| 362 | } | 354 | } |
| 363 | 355 | ||
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h index 140263b09..9d92d4af0 100644 --- a/src/core/cpu_manager.h +++ b/src/core/cpu_manager.h | |||
| @@ -78,9 +78,9 @@ private: | |||
| 78 | void SingleCoreRunSuspendThread(); | 78 | void SingleCoreRunSuspendThread(); |
| 79 | void SingleCorePause(bool paused); | 79 | void SingleCorePause(bool paused); |
| 80 | 80 | ||
| 81 | static void ThreadStart(CpuManager& cpu_manager, std::size_t core); | 81 | static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core); |
| 82 | 82 | ||
| 83 | void RunThread(std::size_t core); | 83 | void RunThread(std::stop_token stop_token, std::size_t core); |
| 84 | 84 | ||
| 85 | struct CoreData { | 85 | struct CoreData { |
| 86 | std::shared_ptr<Common::Fiber> host_context; | 86 | std::shared_ptr<Common::Fiber> host_context; |
| @@ -89,7 +89,7 @@ private: | |||
| 89 | std::atomic<bool> is_running; | 89 | std::atomic<bool> is_running; |
| 90 | std::atomic<bool> is_paused; | 90 | std::atomic<bool> is_paused; |
| 91 | std::atomic<bool> initialized; | 91 | std::atomic<bool> initialized; |
| 92 | std::unique_ptr<std::thread> host_thread; | 92 | std::jthread host_thread; |
| 93 | }; | 93 | }; |
| 94 | 94 | ||
| 95 | std::atomic<bool> running_mode{}; | 95 | std::atomic<bool> running_mode{}; |
diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index 1b429bc1e..6771ef621 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp | |||
| @@ -28,7 +28,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) { | |||
| 28 | 28 | ||
| 29 | bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { | 29 | bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) { |
| 30 | auto& monitor = system.Monitor(); | 30 | auto& monitor = system.Monitor(); |
| 31 | const auto current_core = system.CurrentCoreIndex(); | 31 | const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); |
| 32 | 32 | ||
| 33 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. | 33 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. |
| 34 | // TODO(bunnei): We should call CanAccessAtomic(..) here. | 34 | // TODO(bunnei): We should call CanAccessAtomic(..) here. |
| @@ -58,7 +58,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu | |||
| 58 | 58 | ||
| 59 | bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { | 59 | bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) { |
| 60 | auto& monitor = system.Monitor(); | 60 | auto& monitor = system.Monitor(); |
| 61 | const auto current_core = system.CurrentCoreIndex(); | 61 | const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); |
| 62 | 62 | ||
| 63 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. | 63 | // TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable. |
| 64 | // TODO(bunnei): We should call CanAccessAtomic(..) here. | 64 | // TODO(bunnei): We should call CanAccessAtomic(..) here. |
diff --git a/src/core/hle/kernel/k_auto_object.h b/src/core/hle/kernel/k_auto_object.h index e4fcdbc67..165b76747 100644 --- a/src/core/hle/kernel/k_auto_object.h +++ b/src/core/hle/kernel/k_auto_object.h | |||
| @@ -170,6 +170,10 @@ public: | |||
| 170 | } | 170 | } |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | const std::string& GetName() const { | ||
| 174 | return name; | ||
| 175 | } | ||
| 176 | |||
| 173 | private: | 177 | private: |
| 174 | void RegisterWithKernel(); | 178 | void RegisterWithKernel(); |
| 175 | void UnregisterWithKernel(); | 179 | void UnregisterWithKernel(); |
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index ef14ad1d2..4174f35fd 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp | |||
| @@ -35,7 +35,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) { | |||
| 35 | bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, | 35 | bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero, |
| 36 | u32 new_orr_mask) { | 36 | u32 new_orr_mask) { |
| 37 | auto& monitor = system.Monitor(); | 37 | auto& monitor = system.Monitor(); |
| 38 | const auto current_core = system.CurrentCoreIndex(); | 38 | const auto current_core = system.Kernel().CurrentPhysicalCoreIndex(); |
| 39 | 39 | ||
| 40 | // Load the value from the address. | 40 | // Load the value from the address. |
| 41 | const auto expected = monitor.ExclusiveRead32(current_core, address); | 41 | const auto expected = monitor.ExclusiveRead32(current_core, address); |
diff --git a/src/core/hle/kernel/k_handle_table.cpp b/src/core/hle/kernel/k_handle_table.cpp index 6a420d5b0..d720c2dda 100644 --- a/src/core/hle/kernel/k_handle_table.cpp +++ b/src/core/hle/kernel/k_handle_table.cpp | |||
| @@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() { | |||
| 13 | // Get the table and clear our record of it. | 13 | // Get the table and clear our record of it. |
| 14 | u16 saved_table_size = 0; | 14 | u16 saved_table_size = 0; |
| 15 | { | 15 | { |
| 16 | KScopedDisableDispatch dd(kernel); | ||
| 16 | KScopedSpinLock lk(m_lock); | 17 | KScopedSpinLock lk(m_lock); |
| 17 | 18 | ||
| 18 | std::swap(m_table_size, saved_table_size); | 19 | std::swap(m_table_size, saved_table_size); |
| @@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) { | |||
| 43 | // Find the object and free the entry. | 44 | // Find the object and free the entry. |
| 44 | KAutoObject* obj = nullptr; | 45 | KAutoObject* obj = nullptr; |
| 45 | { | 46 | { |
| 47 | KScopedDisableDispatch dd(kernel); | ||
| 46 | KScopedSpinLock lk(m_lock); | 48 | KScopedSpinLock lk(m_lock); |
| 47 | 49 | ||
| 48 | if (this->IsValidHandle(handle)) { | 50 | if (this->IsValidHandle(handle)) { |
| @@ -61,6 +63,7 @@ bool KHandleTable::Remove(Handle handle) { | |||
| 61 | } | 63 | } |
| 62 | 64 | ||
| 63 | ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { | 65 | ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { |
| 66 | KScopedDisableDispatch dd(kernel); | ||
| 64 | KScopedSpinLock lk(m_lock); | 67 | KScopedSpinLock lk(m_lock); |
| 65 | 68 | ||
| 66 | // Never exceed our capacity. | 69 | // Never exceed our capacity. |
| @@ -83,6 +86,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) { | |||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 85 | ResultCode KHandleTable::Reserve(Handle* out_handle) { | 88 | ResultCode KHandleTable::Reserve(Handle* out_handle) { |
| 89 | KScopedDisableDispatch dd(kernel); | ||
| 86 | KScopedSpinLock lk(m_lock); | 90 | KScopedSpinLock lk(m_lock); |
| 87 | 91 | ||
| 88 | // Never exceed our capacity. | 92 | // Never exceed our capacity. |
| @@ -93,6 +97,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) { | |||
| 93 | } | 97 | } |
| 94 | 98 | ||
| 95 | void KHandleTable::Unreserve(Handle handle) { | 99 | void KHandleTable::Unreserve(Handle handle) { |
| 100 | KScopedDisableDispatch dd(kernel); | ||
| 96 | KScopedSpinLock lk(m_lock); | 101 | KScopedSpinLock lk(m_lock); |
| 97 | 102 | ||
| 98 | // Unpack the handle. | 103 | // Unpack the handle. |
| @@ -111,6 +116,7 @@ void KHandleTable::Unreserve(Handle handle) { | |||
| 111 | } | 116 | } |
| 112 | 117 | ||
| 113 | void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { | 118 | void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) { |
| 119 | KScopedDisableDispatch dd(kernel); | ||
| 114 | KScopedSpinLock lk(m_lock); | 120 | KScopedSpinLock lk(m_lock); |
| 115 | 121 | ||
| 116 | // Unpack the handle. | 122 | // Unpack the handle. |
diff --git a/src/core/hle/kernel/k_handle_table.h b/src/core/hle/kernel/k_handle_table.h index 2ff6aa160..75dcec7df 100644 --- a/src/core/hle/kernel/k_handle_table.h +++ b/src/core/hle/kernel/k_handle_table.h | |||
| @@ -69,6 +69,7 @@ public: | |||
| 69 | template <typename T = KAutoObject> | 69 | template <typename T = KAutoObject> |
| 70 | KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { | 70 | KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const { |
| 71 | // Lock and look up in table. | 71 | // Lock and look up in table. |
| 72 | KScopedDisableDispatch dd(kernel); | ||
| 72 | KScopedSpinLock lk(m_lock); | 73 | KScopedSpinLock lk(m_lock); |
| 73 | 74 | ||
| 74 | if constexpr (std::is_same_v<T, KAutoObject>) { | 75 | if constexpr (std::is_same_v<T, KAutoObject>) { |
| @@ -123,6 +124,7 @@ public: | |||
| 123 | size_t num_opened; | 124 | size_t num_opened; |
| 124 | { | 125 | { |
| 125 | // Lock the table. | 126 | // Lock the table. |
| 127 | KScopedDisableDispatch dd(kernel); | ||
| 126 | KScopedSpinLock lk(m_lock); | 128 | KScopedSpinLock lk(m_lock); |
| 127 | for (num_opened = 0; num_opened < num_handles; num_opened++) { | 129 | for (num_opened = 0; num_opened < num_handles; num_opened++) { |
| 128 | // Get the current handle. | 130 | // Get the current handle. |
diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index 8ead1a769..3d7e6707e 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp | |||
| @@ -59,6 +59,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority | |||
| 59 | thread->GetContext64().cpu_registers[0] = 0; | 59 | thread->GetContext64().cpu_registers[0] = 0; |
| 60 | thread->GetContext32().cpu_registers[1] = thread_handle; | 60 | thread->GetContext32().cpu_registers[1] = thread_handle; |
| 61 | thread->GetContext64().cpu_registers[1] = thread_handle; | 61 | thread->GetContext64().cpu_registers[1] = thread_handle; |
| 62 | thread->DisableDispatch(); | ||
| 62 | 63 | ||
| 63 | auto& kernel = system.Kernel(); | 64 | auto& kernel = system.Kernel(); |
| 64 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires | 65 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires |
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 6a7d80d03..6ddbae52c 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -376,20 +376,18 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) { | |||
| 376 | } | 376 | } |
| 377 | 377 | ||
| 378 | void KScheduler::DisableScheduling(KernelCore& kernel) { | 378 | void KScheduler::DisableScheduling(KernelCore& kernel) { |
| 379 | if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { | 379 | ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0); |
| 380 | ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0); | 380 | GetCurrentThreadPointer(kernel)->DisableDispatch(); |
| 381 | scheduler->GetCurrentThread()->DisableDispatch(); | ||
| 382 | } | ||
| 383 | } | 381 | } |
| 384 | 382 | ||
| 385 | void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { | 383 | void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { |
| 386 | if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { | 384 | ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 1); |
| 387 | ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1); | 385 | |
| 388 | if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) { | 386 | if (GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() > 1) { |
| 389 | scheduler->GetCurrentThread()->EnableDispatch(); | 387 | GetCurrentThreadPointer(kernel)->EnableDispatch(); |
| 390 | } | 388 | } else { |
| 389 | RescheduleCores(kernel, cores_needing_scheduling); | ||
| 391 | } | 390 | } |
| 392 | RescheduleCores(kernel, cores_needing_scheduling); | ||
| 393 | } | 391 | } |
| 394 | 392 | ||
| 395 | u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { | 393 | u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { |
| @@ -617,13 +615,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c | |||
| 617 | state.highest_priority_thread = nullptr; | 615 | state.highest_priority_thread = nullptr; |
| 618 | } | 616 | } |
| 619 | 617 | ||
| 620 | KScheduler::~KScheduler() { | 618 | void KScheduler::Finalize() { |
| 621 | if (idle_thread) { | 619 | if (idle_thread) { |
| 622 | idle_thread->Close(); | 620 | idle_thread->Close(); |
| 623 | idle_thread = nullptr; | 621 | idle_thread = nullptr; |
| 624 | } | 622 | } |
| 625 | } | 623 | } |
| 626 | 624 | ||
| 625 | KScheduler::~KScheduler() { | ||
| 626 | ASSERT(!idle_thread); | ||
| 627 | } | ||
| 628 | |||
| 627 | KThread* KScheduler::GetCurrentThread() const { | 629 | KThread* KScheduler::GetCurrentThread() const { |
| 628 | if (auto result = current_thread.load(); result) { | 630 | if (auto result = current_thread.load(); result) { |
| 629 | return result; | 631 | return result; |
| @@ -642,10 +644,12 @@ void KScheduler::RescheduleCurrentCore() { | |||
| 642 | if (phys_core.IsInterrupted()) { | 644 | if (phys_core.IsInterrupted()) { |
| 643 | phys_core.ClearInterrupt(); | 645 | phys_core.ClearInterrupt(); |
| 644 | } | 646 | } |
| 647 | |||
| 645 | guard.Lock(); | 648 | guard.Lock(); |
| 646 | if (state.needs_scheduling.load()) { | 649 | if (state.needs_scheduling.load()) { |
| 647 | Schedule(); | 650 | Schedule(); |
| 648 | } else { | 651 | } else { |
| 652 | GetCurrentThread()->EnableDispatch(); | ||
| 649 | guard.Unlock(); | 653 | guard.Unlock(); |
| 650 | } | 654 | } |
| 651 | } | 655 | } |
| @@ -655,26 +659,33 @@ void KScheduler::OnThreadStart() { | |||
| 655 | } | 659 | } |
| 656 | 660 | ||
| 657 | void KScheduler::Unload(KThread* thread) { | 661 | void KScheduler::Unload(KThread* thread) { |
| 662 | ASSERT(thread); | ||
| 663 | |||
| 658 | LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); | 664 | LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); |
| 659 | 665 | ||
| 660 | if (thread) { | 666 | if (thread->IsCallingSvc()) { |
| 661 | if (thread->IsCallingSvc()) { | 667 | thread->ClearIsCallingSvc(); |
| 662 | thread->ClearIsCallingSvc(); | ||
| 663 | } | ||
| 664 | if (!thread->IsTerminationRequested()) { | ||
| 665 | prev_thread = thread; | ||
| 666 | |||
| 667 | Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); | ||
| 668 | cpu_core.SaveContext(thread->GetContext32()); | ||
| 669 | cpu_core.SaveContext(thread->GetContext64()); | ||
| 670 | // Save the TPIDR_EL0 system register in case it was modified. | ||
| 671 | thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); | ||
| 672 | cpu_core.ClearExclusiveState(); | ||
| 673 | } else { | ||
| 674 | prev_thread = nullptr; | ||
| 675 | } | ||
| 676 | thread->context_guard.Unlock(); | ||
| 677 | } | 668 | } |
| 669 | |||
| 670 | auto& physical_core = system.Kernel().PhysicalCore(core_id); | ||
| 671 | if (!physical_core.IsInitialized()) { | ||
| 672 | return; | ||
| 673 | } | ||
| 674 | |||
| 675 | Core::ARM_Interface& cpu_core = physical_core.ArmInterface(); | ||
| 676 | cpu_core.SaveContext(thread->GetContext32()); | ||
| 677 | cpu_core.SaveContext(thread->GetContext64()); | ||
| 678 | // Save the TPIDR_EL0 system register in case it was modified. | ||
| 679 | thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); | ||
| 680 | cpu_core.ClearExclusiveState(); | ||
| 681 | |||
| 682 | if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) { | ||
| 683 | prev_thread = thread; | ||
| 684 | } else { | ||
| 685 | prev_thread = nullptr; | ||
| 686 | } | ||
| 687 | |||
| 688 | thread->context_guard.Unlock(); | ||
| 678 | } | 689 | } |
| 679 | 690 | ||
| 680 | void KScheduler::Reload(KThread* thread) { | 691 | void KScheduler::Reload(KThread* thread) { |
| @@ -683,11 +694,6 @@ void KScheduler::Reload(KThread* thread) { | |||
| 683 | if (thread) { | 694 | if (thread) { |
| 684 | ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); | 695 | ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); |
| 685 | 696 | ||
| 686 | auto* const thread_owner_process = thread->GetOwnerProcess(); | ||
| 687 | if (thread_owner_process != nullptr) { | ||
| 688 | system.Kernel().MakeCurrentProcess(thread_owner_process); | ||
| 689 | } | ||
| 690 | |||
| 691 | Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); | 697 | Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); |
| 692 | cpu_core.LoadContext(thread->GetContext32()); | 698 | cpu_core.LoadContext(thread->GetContext32()); |
| 693 | cpu_core.LoadContext(thread->GetContext64()); | 699 | cpu_core.LoadContext(thread->GetContext64()); |
| @@ -705,7 +711,7 @@ void KScheduler::SwitchContextStep2() { | |||
| 705 | } | 711 | } |
| 706 | 712 | ||
| 707 | void KScheduler::ScheduleImpl() { | 713 | void KScheduler::ScheduleImpl() { |
| 708 | KThread* previous_thread = current_thread.load(); | 714 | KThread* previous_thread = GetCurrentThread(); |
| 709 | KThread* next_thread = state.highest_priority_thread; | 715 | KThread* next_thread = state.highest_priority_thread; |
| 710 | 716 | ||
| 711 | state.needs_scheduling = false; | 717 | state.needs_scheduling = false; |
| @@ -717,10 +723,15 @@ void KScheduler::ScheduleImpl() { | |||
| 717 | 723 | ||
| 718 | // If we're not actually switching thread, there's nothing to do. | 724 | // If we're not actually switching thread, there's nothing to do. |
| 719 | if (next_thread == current_thread.load()) { | 725 | if (next_thread == current_thread.load()) { |
| 726 | previous_thread->EnableDispatch(); | ||
| 720 | guard.Unlock(); | 727 | guard.Unlock(); |
| 721 | return; | 728 | return; |
| 722 | } | 729 | } |
| 723 | 730 | ||
| 731 | if (next_thread->GetCurrentCore() != core_id) { | ||
| 732 | next_thread->SetCurrentCore(core_id); | ||
| 733 | } | ||
| 734 | |||
| 724 | current_thread.store(next_thread); | 735 | current_thread.store(next_thread); |
| 725 | 736 | ||
| 726 | KProcess* const previous_process = system.Kernel().CurrentProcess(); | 737 | KProcess* const previous_process = system.Kernel().CurrentProcess(); |
| @@ -731,11 +742,7 @@ void KScheduler::ScheduleImpl() { | |||
| 731 | Unload(previous_thread); | 742 | Unload(previous_thread); |
| 732 | 743 | ||
| 733 | std::shared_ptr<Common::Fiber>* old_context; | 744 | std::shared_ptr<Common::Fiber>* old_context; |
| 734 | if (previous_thread != nullptr) { | 745 | old_context = &previous_thread->GetHostContext(); |
| 735 | old_context = &previous_thread->GetHostContext(); | ||
| 736 | } else { | ||
| 737 | old_context = &idle_thread->GetHostContext(); | ||
| 738 | } | ||
| 739 | guard.Unlock(); | 746 | guard.Unlock(); |
| 740 | 747 | ||
| 741 | Common::Fiber::YieldTo(*old_context, *switch_fiber); | 748 | Common::Fiber::YieldTo(*old_context, *switch_fiber); |
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 12cfae919..516e0cdba 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h | |||
| @@ -33,6 +33,8 @@ public: | |||
| 33 | explicit KScheduler(Core::System& system_, s32 core_id_); | 33 | explicit KScheduler(Core::System& system_, s32 core_id_); |
| 34 | ~KScheduler(); | 34 | ~KScheduler(); |
| 35 | 35 | ||
| 36 | void Finalize(); | ||
| 37 | |||
| 36 | /// Reschedules to the next available thread (call after current thread is suspended) | 38 | /// Reschedules to the next available thread (call after current thread is suspended) |
| 37 | void RescheduleCurrentCore(); | 39 | void RescheduleCurrentCore(); |
| 38 | 40 | ||
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 9f1d3156b..0f6808ade 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "common/fiber.h" | 14 | #include "common/fiber.h" |
| 15 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 16 | #include "common/scope_exit.h" | 16 | #include "common/scope_exit.h" |
| 17 | #include "common/settings.h" | ||
| 17 | #include "common/thread_queue_list.h" | 18 | #include "common/thread_queue_list.h" |
| 18 | #include "core/core.h" | 19 | #include "core/core.h" |
| 19 | #include "core/cpu_manager.h" | 20 | #include "core/cpu_manager.h" |
| @@ -188,7 +189,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s | |||
| 188 | // Setup the stack parameters. | 189 | // Setup the stack parameters. |
| 189 | StackParameters& sp = GetStackParameters(); | 190 | StackParameters& sp = GetStackParameters(); |
| 190 | sp.cur_thread = this; | 191 | sp.cur_thread = this; |
| 191 | sp.disable_count = 1; | 192 | sp.disable_count = 0; |
| 192 | SetInExceptionHandler(); | 193 | SetInExceptionHandler(); |
| 193 | 194 | ||
| 194 | // Set thread ID. | 195 | // Set thread ID. |
| @@ -215,9 +216,10 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint | |||
| 215 | // Initialize the thread. | 216 | // Initialize the thread. |
| 216 | R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); | 217 | R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); |
| 217 | 218 | ||
| 218 | // Initialize host context. | 219 | // Initialize emulation parameters. |
| 219 | thread->host_context = | 220 | thread->host_context = |
| 220 | std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); | 221 | std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter); |
| 222 | thread->is_single_core = !Settings::values.use_multi_core.GetValue(); | ||
| 221 | 223 | ||
| 222 | return ResultSuccess; | 224 | return ResultSuccess; |
| 223 | } | 225 | } |
| @@ -970,6 +972,9 @@ ResultCode KThread::Run() { | |||
| 970 | 972 | ||
| 971 | // Set our state and finish. | 973 | // Set our state and finish. |
| 972 | SetState(ThreadState::Runnable); | 974 | SetState(ThreadState::Runnable); |
| 975 | |||
| 976 | DisableDispatch(); | ||
| 977 | |||
| 973 | return ResultSuccess; | 978 | return ResultSuccess; |
| 974 | } | 979 | } |
| 975 | } | 980 | } |
| @@ -1054,4 +1059,16 @@ s32 GetCurrentCoreId(KernelCore& kernel) { | |||
| 1054 | return GetCurrentThread(kernel).GetCurrentCore(); | 1059 | return GetCurrentThread(kernel).GetCurrentCore(); |
| 1055 | } | 1060 | } |
| 1056 | 1061 | ||
| 1062 | KScopedDisableDispatch::~KScopedDisableDispatch() { | ||
| 1063 | if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) { | ||
| 1064 | auto scheduler = kernel.CurrentScheduler(); | ||
| 1065 | |||
| 1066 | if (scheduler) { | ||
| 1067 | scheduler->RescheduleCurrentCore(); | ||
| 1068 | } | ||
| 1069 | } else { | ||
| 1070 | GetCurrentThread(kernel).EnableDispatch(); | ||
| 1071 | } | ||
| 1072 | } | ||
| 1073 | |||
| 1057 | } // namespace Kernel | 1074 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index c77f44ad4..e4c4c877d 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -450,16 +450,39 @@ public: | |||
| 450 | sleeping_queue = q; | 450 | sleeping_queue = q; |
| 451 | } | 451 | } |
| 452 | 452 | ||
| 453 | [[nodiscard]] bool IsKernelThread() const { | ||
| 454 | return GetActiveCore() == 3; | ||
| 455 | } | ||
| 456 | |||
| 457 | [[nodiscard]] bool IsDispatchTrackingDisabled() const { | ||
| 458 | return is_single_core || IsKernelThread(); | ||
| 459 | } | ||
| 460 | |||
| 453 | [[nodiscard]] s32 GetDisableDispatchCount() const { | 461 | [[nodiscard]] s32 GetDisableDispatchCount() const { |
| 462 | if (IsDispatchTrackingDisabled()) { | ||
| 463 | // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. | ||
| 464 | return 1; | ||
| 465 | } | ||
| 466 | |||
| 454 | return this->GetStackParameters().disable_count; | 467 | return this->GetStackParameters().disable_count; |
| 455 | } | 468 | } |
| 456 | 469 | ||
| 457 | void DisableDispatch() { | 470 | void DisableDispatch() { |
| 471 | if (IsDispatchTrackingDisabled()) { | ||
| 472 | // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. | ||
| 473 | return; | ||
| 474 | } | ||
| 475 | |||
| 458 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); | 476 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); |
| 459 | this->GetStackParameters().disable_count++; | 477 | this->GetStackParameters().disable_count++; |
| 460 | } | 478 | } |
| 461 | 479 | ||
| 462 | void EnableDispatch() { | 480 | void EnableDispatch() { |
| 481 | if (IsDispatchTrackingDisabled()) { | ||
| 482 | // TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch. | ||
| 483 | return; | ||
| 484 | } | ||
| 485 | |||
| 463 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); | 486 | ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); |
| 464 | this->GetStackParameters().disable_count--; | 487 | this->GetStackParameters().disable_count--; |
| 465 | } | 488 | } |
| @@ -708,6 +731,7 @@ private: | |||
| 708 | 731 | ||
| 709 | // For emulation | 732 | // For emulation |
| 710 | std::shared_ptr<Common::Fiber> host_context{}; | 733 | std::shared_ptr<Common::Fiber> host_context{}; |
| 734 | bool is_single_core{}; | ||
| 711 | 735 | ||
| 712 | // For debugging | 736 | // For debugging |
| 713 | std::vector<KSynchronizationObject*> wait_objects_for_debugging; | 737 | std::vector<KSynchronizationObject*> wait_objects_for_debugging; |
| @@ -752,4 +776,16 @@ public: | |||
| 752 | } | 776 | } |
| 753 | }; | 777 | }; |
| 754 | 778 | ||
| 779 | class KScopedDisableDispatch { | ||
| 780 | public: | ||
| 781 | [[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} { | ||
| 782 | GetCurrentThread(kernel).DisableDispatch(); | ||
| 783 | } | ||
| 784 | |||
| 785 | ~KScopedDisableDispatch(); | ||
| 786 | |||
| 787 | private: | ||
| 788 | KernelCore& kernel; | ||
| 789 | }; | ||
| 790 | |||
| 755 | } // namespace Kernel | 791 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index bea945301..8fdab44e4 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -85,8 +85,9 @@ struct KernelCore::Impl { | |||
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | void InitializeCores() { | 87 | void InitializeCores() { |
| 88 | for (auto& core : cores) { | 88 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { |
| 89 | core.Initialize(current_process->Is64BitProcess()); | 89 | cores[core_id].Initialize(current_process->Is64BitProcess()); |
| 90 | system.Memory().SetCurrentPageTable(*current_process, core_id); | ||
| 90 | } | 91 | } |
| 91 | } | 92 | } |
| 92 | 93 | ||
| @@ -131,15 +132,6 @@ struct KernelCore::Impl { | |||
| 131 | next_user_process_id = KProcess::ProcessIDMin; | 132 | next_user_process_id = KProcess::ProcessIDMin; |
| 132 | next_thread_id = 1; | 133 | next_thread_id = 1; |
| 133 | 134 | ||
| 134 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | ||
| 135 | if (suspend_threads[core_id]) { | ||
| 136 | suspend_threads[core_id]->Close(); | ||
| 137 | suspend_threads[core_id] = nullptr; | ||
| 138 | } | ||
| 139 | |||
| 140 | schedulers[core_id].reset(); | ||
| 141 | } | ||
| 142 | |||
| 143 | cores.clear(); | 135 | cores.clear(); |
| 144 | 136 | ||
| 145 | global_handle_table->Finalize(); | 137 | global_handle_table->Finalize(); |
| @@ -167,6 +159,16 @@ struct KernelCore::Impl { | |||
| 167 | CleanupObject(time_shared_mem); | 159 | CleanupObject(time_shared_mem); |
| 168 | CleanupObject(system_resource_limit); | 160 | CleanupObject(system_resource_limit); |
| 169 | 161 | ||
| 162 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | ||
| 163 | if (suspend_threads[core_id]) { | ||
| 164 | suspend_threads[core_id]->Close(); | ||
| 165 | suspend_threads[core_id] = nullptr; | ||
| 166 | } | ||
| 167 | |||
| 168 | schedulers[core_id]->Finalize(); | ||
| 169 | schedulers[core_id].reset(); | ||
| 170 | } | ||
| 171 | |||
| 170 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others | 172 | // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others |
| 171 | next_host_thread_id = Core::Hardware::NUM_CPU_CORES; | 173 | next_host_thread_id = Core::Hardware::NUM_CPU_CORES; |
| 172 | 174 | ||
| @@ -257,14 +259,6 @@ struct KernelCore::Impl { | |||
| 257 | 259 | ||
| 258 | void MakeCurrentProcess(KProcess* process) { | 260 | void MakeCurrentProcess(KProcess* process) { |
| 259 | current_process = process; | 261 | current_process = process; |
| 260 | if (process == nullptr) { | ||
| 261 | return; | ||
| 262 | } | ||
| 263 | |||
| 264 | const u32 core_id = GetCurrentHostThreadID(); | ||
| 265 | if (core_id < Core::Hardware::NUM_CPU_CORES) { | ||
| 266 | system.Memory().SetCurrentPageTable(*process, core_id); | ||
| 267 | } | ||
| 268 | } | 262 | } |
| 269 | 263 | ||
| 270 | static inline thread_local u32 host_thread_id = UINT32_MAX; | 264 | static inline thread_local u32 host_thread_id = UINT32_MAX; |
| @@ -827,16 +821,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const { | |||
| 827 | return impl->cores[id]; | 821 | return impl->cores[id]; |
| 828 | } | 822 | } |
| 829 | 823 | ||
| 824 | size_t KernelCore::CurrentPhysicalCoreIndex() const { | ||
| 825 | const u32 core_id = impl->GetCurrentHostThreadID(); | ||
| 826 | if (core_id >= Core::Hardware::NUM_CPU_CORES) { | ||
| 827 | return Core::Hardware::NUM_CPU_CORES - 1; | ||
| 828 | } | ||
| 829 | return core_id; | ||
| 830 | } | ||
| 831 | |||
| 830 | Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { | 832 | Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() { |
| 831 | u32 core_id = impl->GetCurrentHostThreadID(); | 833 | return impl->cores[CurrentPhysicalCoreIndex()]; |
| 832 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 833 | return impl->cores[core_id]; | ||
| 834 | } | 834 | } |
| 835 | 835 | ||
| 836 | const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { | 836 | const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const { |
| 837 | u32 core_id = impl->GetCurrentHostThreadID(); | 837 | return impl->cores[CurrentPhysicalCoreIndex()]; |
| 838 | ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||
| 839 | return impl->cores[core_id]; | ||
| 840 | } | 838 | } |
| 841 | 839 | ||
| 842 | Kernel::KScheduler* KernelCore::CurrentScheduler() { | 840 | Kernel::KScheduler* KernelCore::CurrentScheduler() { |
| @@ -1029,6 +1027,9 @@ void KernelCore::Suspend(bool in_suspention) { | |||
| 1029 | impl->suspend_threads[core_id]->SetState(state); | 1027 | impl->suspend_threads[core_id]->SetState(state); |
| 1030 | impl->suspend_threads[core_id]->SetWaitReasonForDebugging( | 1028 | impl->suspend_threads[core_id]->SetWaitReasonForDebugging( |
| 1031 | ThreadWaitReasonForDebugging::Suspended); | 1029 | ThreadWaitReasonForDebugging::Suspended); |
| 1030 | if (!should_suspend) { | ||
| 1031 | impl->suspend_threads[core_id]->DisableDispatch(); | ||
| 1032 | } | ||
| 1032 | } | 1033 | } |
| 1033 | } | 1034 | } |
| 1034 | } | 1035 | } |
| @@ -1043,13 +1044,11 @@ void KernelCore::ExceptionalExit() { | |||
| 1043 | } | 1044 | } |
| 1044 | 1045 | ||
| 1045 | void KernelCore::EnterSVCProfile() { | 1046 | void KernelCore::EnterSVCProfile() { |
| 1046 | std::size_t core = impl->GetCurrentHostThreadID(); | 1047 | impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC)); |
| 1047 | impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC)); | ||
| 1048 | } | 1048 | } |
| 1049 | 1049 | ||
| 1050 | void KernelCore::ExitSVCProfile() { | 1050 | void KernelCore::ExitSVCProfile() { |
| 1051 | std::size_t core = impl->GetCurrentHostThreadID(); | 1051 | MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]); |
| 1052 | MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); | ||
| 1053 | } | 1052 | } |
| 1054 | 1053 | ||
| 1055 | std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { | 1054 | std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) { |
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 3a6db0b1c..57535433b 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -146,6 +146,9 @@ public: | |||
| 146 | /// Gets the an instance of the respective physical CPU core. | 146 | /// Gets the an instance of the respective physical CPU core. |
| 147 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; | 147 | const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; |
| 148 | 148 | ||
| 149 | /// Gets the current physical core index for the running host thread. | ||
| 150 | std::size_t CurrentPhysicalCoreIndex() const; | ||
| 151 | |||
| 149 | /// Gets the sole instance of the Scheduler at the current running core. | 152 | /// Gets the sole instance of the Scheduler at the current running core. |
| 150 | Kernel::KScheduler* CurrentScheduler(); | 153 | Kernel::KScheduler* CurrentScheduler(); |
| 151 | 154 | ||
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 2eb532472..890c52198 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -877,7 +877,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle | |||
| 877 | const u64 thread_ticks = current_thread->GetCpuTime(); | 877 | const u64 thread_ticks = current_thread->GetCpuTime(); |
| 878 | 878 | ||
| 879 | out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); | 879 | out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); |
| 880 | } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { | 880 | } else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) { |
| 881 | out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; | 881 | out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks; |
| 882 | } | 882 | } |
| 883 | 883 | ||
| @@ -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 | ||
| 17 | namespace Service::AM::Applets { | 17 | namespace Service::AM::Applets { |
| 18 | 18 | ||
| 19 | struct 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 | }; | ||
| 41 | static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size."); | ||
| 42 | |||
| 19 | #pragma pack(push, 4) | 43 | #pragma pack(push, 4) |
| 20 | struct ShowError { | 44 | struct ShowError { |
| 21 | u8 mode; | 45 | u8 mode; |
| @@ -76,12 +100,7 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) { | |||
| 76 | } | 100 | } |
| 77 | 101 | ||
| 78 | ResultCode Decode64BitError(u64 error) { | 102 | ResultCode 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/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index e742db48f..0a53c0c81 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 | ||
| 15 | namespace Service::NIFM { | 16 | namespace 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 | ||
| @@ -322,12 +323,15 @@ private: | |||
| 322 | void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { | 323 | void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) { |
| 323 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 324 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 324 | 325 | ||
| 325 | const auto [ipv4, error] = Network::GetHostIPv4Address(); | 326 | auto ipv4 = Network::GetHostIPv4Address(); |
| 326 | UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS); | 327 | if (!ipv4) { |
| 328 | LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0"); | ||
| 329 | ipv4.emplace(Network::IPv4Address{0, 0, 0, 0}); | ||
| 330 | } | ||
| 327 | 331 | ||
| 328 | IPC::ResponseBuilder rb{ctx, 3}; | 332 | IPC::ResponseBuilder rb{ctx, 3}; |
| 329 | rb.Push(ResultSuccess); | 333 | rb.Push(ResultSuccess); |
| 330 | rb.PushRaw(ipv4); | 334 | rb.PushRaw(*ipv4); |
| 331 | } | 335 | } |
| 332 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { | 336 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 333 | LOG_DEBUG(Service_NIFM, "called"); | 337 | LOG_DEBUG(Service_NIFM, "called"); |
| @@ -354,10 +358,10 @@ private: | |||
| 354 | static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), | 358 | static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), |
| 355 | "IpConfigInfo has incorrect size."); | 359 | "IpConfigInfo has incorrect size."); |
| 356 | 360 | ||
| 357 | const IpConfigInfo ip_config_info{ | 361 | IpConfigInfo ip_config_info{ |
| 358 | .ip_address_setting{ | 362 | .ip_address_setting{ |
| 359 | .is_automatic{true}, | 363 | .is_automatic{true}, |
| 360 | .current_address{192, 168, 1, 100}, | 364 | .current_address{0, 0, 0, 0}, |
| 361 | .subnet_mask{255, 255, 255, 0}, | 365 | .subnet_mask{255, 255, 255, 0}, |
| 362 | .gateway{192, 168, 1, 1}, | 366 | .gateway{192, 168, 1, 1}, |
| 363 | }, | 367 | }, |
| @@ -368,6 +372,19 @@ private: | |||
| 368 | }, | 372 | }, |
| 369 | }; | 373 | }; |
| 370 | 374 | ||
| 375 | const auto iface = Network::GetSelectedNetworkInterface(); | ||
| 376 | if (iface) { | ||
| 377 | ip_config_info.ip_address_setting = | ||
| 378 | IpAddressSetting{.is_automatic{true}, | ||
| 379 | .current_address{Network::TranslateIPv4(iface->ip_address)}, | ||
| 380 | .subnet_mask{Network::TranslateIPv4(iface->subnet_mask)}, | ||
| 381 | .gateway{Network::TranslateIPv4(iface->gateway)}}; | ||
| 382 | |||
| 383 | } else { | ||
| 384 | LOG_ERROR(Service_NIFM, | ||
| 385 | "Couldn't get host network configuration info, using default values"); | ||
| 386 | } | ||
| 387 | |||
| 371 | IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; | 388 | IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)}; |
| 372 | rb.Push(ResultSuccess); | 389 | rb.Push(ResultSuccess); |
| 373 | rb.PushRaw<IpConfigInfo>(ip_config_info); | 390 | rb.PushRaw<IpConfigInfo>(ip_config_info); |
| @@ -384,10 +401,10 @@ private: | |||
| 384 | 401 | ||
| 385 | IPC::ResponseBuilder rb{ctx, 3}; | 402 | IPC::ResponseBuilder rb{ctx, 3}; |
| 386 | rb.Push(ResultSuccess); | 403 | rb.Push(ResultSuccess); |
| 387 | if (Settings::values.bcat_backend.GetValue() == "none") { | 404 | if (Network::GetHostIPv4Address().has_value()) { |
| 388 | rb.Push<u8>(0); | ||
| 389 | } else { | ||
| 390 | rb.Push<u8>(1); | 405 | rb.Push<u8>(1); |
| 406 | } else { | ||
| 407 | rb.Push<u8>(0); | ||
| 391 | } | 408 | } |
| 392 | } | 409 | } |
| 393 | void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { | 410 | void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { |
| @@ -395,10 +412,10 @@ private: | |||
| 395 | 412 | ||
| 396 | IPC::ResponseBuilder rb{ctx, 3}; | 413 | IPC::ResponseBuilder rb{ctx, 3}; |
| 397 | rb.Push(ResultSuccess); | 414 | rb.Push(ResultSuccess); |
| 398 | if (Settings::values.bcat_backend.GetValue() == "none") { | 415 | if (Network::GetHostIPv4Address().has_value()) { |
| 399 | rb.Push<u8>(0); | ||
| 400 | } else { | ||
| 401 | rb.Push<u8>(1); | 416 | rb.Push<u8>(1); |
| 417 | } else { | ||
| 418 | rb.Push<u8>(0); | ||
| 402 | } | 419 | } |
| 403 | } | 420 | } |
| 404 | }; | 421 | }; |
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp index 59ddf6298..b4c3a6099 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue.cpp | |||
| @@ -9,17 +9,20 @@ | |||
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/k_writable_event.h" | 10 | #include "core/hle/kernel/k_writable_event.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/service/kernel_helpers.h" | ||
| 12 | #include "core/hle/service/nvflinger/buffer_queue.h" | 13 | #include "core/hle/service/nvflinger/buffer_queue.h" |
| 13 | 14 | ||
| 14 | namespace Service::NVFlinger { | 15 | namespace Service::NVFlinger { |
| 15 | 16 | ||
| 16 | BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_) | 17 | BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, |
| 17 | : id(id_), layer_id(layer_id_), buffer_wait_event{kernel} { | 18 | KernelHelpers::ServiceContext& service_context_) |
| 18 | Kernel::KAutoObject::Create(std::addressof(buffer_wait_event)); | 19 | : id(id_), layer_id(layer_id_), service_context{service_context_} { |
| 19 | buffer_wait_event.Initialize("BufferQueue:WaitEvent"); | 20 | buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent"); |
| 20 | } | 21 | } |
| 21 | 22 | ||
| 22 | BufferQueue::~BufferQueue() = default; | 23 | BufferQueue::~BufferQueue() { |
| 24 | service_context.CloseEvent(buffer_wait_event); | ||
| 25 | } | ||
| 23 | 26 | ||
| 24 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { | 27 | void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) { |
| 25 | ASSERT(slot < buffer_slots); | 28 | ASSERT(slot < buffer_slots); |
| @@ -41,7 +44,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) | |||
| 41 | .multi_fence = {}, | 44 | .multi_fence = {}, |
| 42 | }; | 45 | }; |
| 43 | 46 | ||
| 44 | buffer_wait_event.GetWritableEvent().Signal(); | 47 | buffer_wait_event->GetWritableEvent().Signal(); |
| 45 | } | 48 | } |
| 46 | 49 | ||
| 47 | std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, | 50 | std::optional<std::pair<u32, Service::Nvidia::MultiFence*>> BufferQueue::DequeueBuffer(u32 width, |
| @@ -119,7 +122,7 @@ void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& mult | |||
| 119 | } | 122 | } |
| 120 | free_buffers_condition.notify_one(); | 123 | free_buffers_condition.notify_one(); |
| 121 | 124 | ||
| 122 | buffer_wait_event.GetWritableEvent().Signal(); | 125 | buffer_wait_event->GetWritableEvent().Signal(); |
| 123 | } | 126 | } |
| 124 | 127 | ||
| 125 | std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { | 128 | std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() { |
| @@ -154,7 +157,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) { | |||
| 154 | } | 157 | } |
| 155 | free_buffers_condition.notify_one(); | 158 | free_buffers_condition.notify_one(); |
| 156 | 159 | ||
| 157 | buffer_wait_event.GetWritableEvent().Signal(); | 160 | buffer_wait_event->GetWritableEvent().Signal(); |
| 158 | } | 161 | } |
| 159 | 162 | ||
| 160 | void BufferQueue::Connect() { | 163 | void BufferQueue::Connect() { |
| @@ -169,7 +172,7 @@ void BufferQueue::Disconnect() { | |||
| 169 | std::unique_lock lock{queue_sequence_mutex}; | 172 | std::unique_lock lock{queue_sequence_mutex}; |
| 170 | queue_sequence.clear(); | 173 | queue_sequence.clear(); |
| 171 | } | 174 | } |
| 172 | buffer_wait_event.GetWritableEvent().Signal(); | 175 | buffer_wait_event->GetWritableEvent().Signal(); |
| 173 | is_connect = false; | 176 | is_connect = false; |
| 174 | free_buffers_condition.notify_one(); | 177 | free_buffers_condition.notify_one(); |
| 175 | } | 178 | } |
| @@ -189,11 +192,11 @@ u32 BufferQueue::Query(QueryType type) { | |||
| 189 | } | 192 | } |
| 190 | 193 | ||
| 191 | Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { | 194 | Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() { |
| 192 | return buffer_wait_event.GetWritableEvent(); | 195 | return buffer_wait_event->GetWritableEvent(); |
| 193 | } | 196 | } |
| 194 | 197 | ||
| 195 | Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { | 198 | Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() { |
| 196 | return buffer_wait_event.GetReadableEvent(); | 199 | return buffer_wait_event->GetReadableEvent(); |
| 197 | } | 200 | } |
| 198 | 201 | ||
| 199 | } // namespace Service::NVFlinger | 202 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 61e337ac5..759247eb0 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h | |||
| @@ -24,6 +24,10 @@ class KReadableEvent; | |||
| 24 | class KWritableEvent; | 24 | class KWritableEvent; |
| 25 | } // namespace Kernel | 25 | } // namespace Kernel |
| 26 | 26 | ||
| 27 | namespace Service::KernelHelpers { | ||
| 28 | class ServiceContext; | ||
| 29 | } // namespace Service::KernelHelpers | ||
| 30 | |||
| 27 | namespace Service::NVFlinger { | 31 | namespace Service::NVFlinger { |
| 28 | 32 | ||
| 29 | constexpr u32 buffer_slots = 0x40; | 33 | constexpr u32 buffer_slots = 0x40; |
| @@ -54,7 +58,8 @@ public: | |||
| 54 | NativeWindowFormat = 2, | 58 | NativeWindowFormat = 2, |
| 55 | }; | 59 | }; |
| 56 | 60 | ||
| 57 | explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_); | 61 | explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_, |
| 62 | KernelHelpers::ServiceContext& service_context_); | ||
| 58 | ~BufferQueue(); | 63 | ~BufferQueue(); |
| 59 | 64 | ||
| 60 | enum class BufferTransformFlags : u32 { | 65 | enum class BufferTransformFlags : u32 { |
| @@ -130,12 +135,14 @@ private: | |||
| 130 | std::list<u32> free_buffers; | 135 | std::list<u32> free_buffers; |
| 131 | std::array<Buffer, buffer_slots> buffers; | 136 | std::array<Buffer, buffer_slots> buffers; |
| 132 | std::list<u32> queue_sequence; | 137 | std::list<u32> queue_sequence; |
| 133 | Kernel::KEvent buffer_wait_event; | 138 | Kernel::KEvent* buffer_wait_event{}; |
| 134 | 139 | ||
| 135 | std::mutex free_buffers_mutex; | 140 | std::mutex free_buffers_mutex; |
| 136 | std::condition_variable free_buffers_condition; | 141 | std::condition_variable free_buffers_condition; |
| 137 | 142 | ||
| 138 | std::mutex queue_sequence_mutex; | 143 | std::mutex queue_sequence_mutex; |
| 144 | |||
| 145 | KernelHelpers::ServiceContext& service_context; | ||
| 139 | }; | 146 | }; |
| 140 | 147 | ||
| 141 | } // namespace Service::NVFlinger | 148 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 941748970..00bff8caf 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -61,12 +61,13 @@ void NVFlinger::SplitVSync() { | |||
| 61 | } | 61 | } |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | NVFlinger::NVFlinger(Core::System& system_) : system(system_) { | 64 | NVFlinger::NVFlinger(Core::System& system_) |
| 65 | displays.emplace_back(0, "Default", system); | 65 | : system(system_), service_context(system_, "nvflinger") { |
| 66 | displays.emplace_back(1, "External", system); | 66 | displays.emplace_back(0, "Default", service_context, system); |
| 67 | displays.emplace_back(2, "Edid", system); | 67 | displays.emplace_back(1, "External", service_context, system); |
| 68 | displays.emplace_back(3, "Internal", system); | 68 | displays.emplace_back(2, "Edid", service_context, system); |
| 69 | displays.emplace_back(4, "Null", system); | 69 | displays.emplace_back(3, "Internal", service_context, system); |
| 70 | displays.emplace_back(4, "Null", service_context, system); | ||
| 70 | guard = std::make_shared<std::mutex>(); | 71 | guard = std::make_shared<std::mutex>(); |
| 71 | 72 | ||
| 72 | // Schedule the screen composition events | 73 | // Schedule the screen composition events |
| @@ -146,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) { | |||
| 146 | void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { | 147 | void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) { |
| 147 | const u32 buffer_queue_id = next_buffer_queue_id++; | 148 | const u32 buffer_queue_id = next_buffer_queue_id++; |
| 148 | buffer_queues.emplace_back( | 149 | buffer_queues.emplace_back( |
| 149 | std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id)); | 150 | std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context)); |
| 150 | display.CreateLayer(layer_id, *buffer_queues.back()); | 151 | display.CreateLayer(layer_id, *buffer_queues.back()); |
| 151 | } | 152 | } |
| 152 | 153 | ||
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index d80fd07ef..6d84cafb4 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <vector> | 15 | #include <vector> |
| 16 | 16 | ||
| 17 | #include "common/common_types.h" | 17 | #include "common/common_types.h" |
| 18 | #include "core/hle/service/kernel_helpers.h" | ||
| 18 | 19 | ||
| 19 | namespace Common { | 20 | namespace Common { |
| 20 | class Event; | 21 | class Event; |
| @@ -135,6 +136,8 @@ private: | |||
| 135 | std::unique_ptr<std::thread> vsync_thread; | 136 | std::unique_ptr<std::thread> vsync_thread; |
| 136 | std::unique_ptr<Common::Event> wait_event; | 137 | std::unique_ptr<Common::Event> wait_event; |
| 137 | std::atomic<bool> is_running{}; | 138 | std::atomic<bool> is_running{}; |
| 139 | |||
| 140 | KernelHelpers::ServiceContext service_context; | ||
| 138 | }; | 141 | }; |
| 139 | 142 | ||
| 140 | } // namespace Service::NVFlinger | 143 | } // namespace Service::NVFlinger |
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index 0dd342dbf..b7705c02a 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp | |||
| @@ -12,18 +12,21 @@ | |||
| 12 | #include "core/hle/kernel/k_event.h" | 12 | #include "core/hle/kernel/k_event.h" |
| 13 | #include "core/hle/kernel/k_readable_event.h" | 13 | #include "core/hle/kernel/k_readable_event.h" |
| 14 | #include "core/hle/kernel/k_writable_event.h" | 14 | #include "core/hle/kernel/k_writable_event.h" |
| 15 | #include "core/hle/service/kernel_helpers.h" | ||
| 15 | #include "core/hle/service/vi/display/vi_display.h" | 16 | #include "core/hle/service/vi/display/vi_display.h" |
| 16 | #include "core/hle/service/vi/layer/vi_layer.h" | 17 | #include "core/hle/service/vi/layer/vi_layer.h" |
| 17 | 18 | ||
| 18 | namespace Service::VI { | 19 | namespace Service::VI { |
| 19 | 20 | ||
| 20 | Display::Display(u64 id, std::string name_, Core::System& system) | 21 | Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, |
| 21 | : display_id{id}, name{std::move(name_)}, vsync_event{system.Kernel()} { | 22 | Core::System& system_) |
| 22 | Kernel::KAutoObject::Create(std::addressof(vsync_event)); | 23 | : display_id{id}, name{std::move(name_)}, service_context{service_context_} { |
| 23 | vsync_event.Initialize(fmt::format("Display VSync Event {}", id)); | 24 | vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 26 | Display::~Display() = default; | 27 | Display::~Display() { |
| 28 | service_context.CloseEvent(vsync_event); | ||
| 29 | } | ||
| 27 | 30 | ||
| 28 | Layer& Display::GetLayer(std::size_t index) { | 31 | Layer& Display::GetLayer(std::size_t index) { |
| 29 | return *layers.at(index); | 32 | return *layers.at(index); |
| @@ -34,11 +37,11 @@ const Layer& Display::GetLayer(std::size_t index) const { | |||
| 34 | } | 37 | } |
| 35 | 38 | ||
| 36 | Kernel::KReadableEvent& Display::GetVSyncEvent() { | 39 | Kernel::KReadableEvent& Display::GetVSyncEvent() { |
| 37 | return vsync_event.GetReadableEvent(); | 40 | return vsync_event->GetReadableEvent(); |
| 38 | } | 41 | } |
| 39 | 42 | ||
| 40 | void Display::SignalVSyncEvent() { | 43 | void Display::SignalVSyncEvent() { |
| 41 | vsync_event.GetWritableEvent().Signal(); | 44 | vsync_event->GetWritableEvent().Signal(); |
| 42 | } | 45 | } |
| 43 | 46 | ||
| 44 | void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { | 47 | void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) { |
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 166f2a4cc..0979fc421 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h | |||
| @@ -18,6 +18,9 @@ class KEvent; | |||
| 18 | namespace Service::NVFlinger { | 18 | namespace Service::NVFlinger { |
| 19 | class BufferQueue; | 19 | class BufferQueue; |
| 20 | } | 20 | } |
| 21 | namespace Service::KernelHelpers { | ||
| 22 | class ServiceContext; | ||
| 23 | } // namespace Service::KernelHelpers | ||
| 21 | 24 | ||
| 22 | namespace Service::VI { | 25 | namespace Service::VI { |
| 23 | 26 | ||
| @@ -31,10 +34,13 @@ class Display { | |||
| 31 | public: | 34 | public: |
| 32 | /// Constructs a display with a given unique ID and name. | 35 | /// Constructs a display with a given unique ID and name. |
| 33 | /// | 36 | /// |
| 34 | /// @param id The unique ID for this display. | 37 | /// @param id The unique ID for this display. |
| 38 | /// @param service_context_ The ServiceContext for the owning service. | ||
| 35 | /// @param name_ The name for this display. | 39 | /// @param name_ The name for this display. |
| 40 | /// @param system_ The global system instance. | ||
| 36 | /// | 41 | /// |
| 37 | Display(u64 id, std::string name_, Core::System& system); | 42 | Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, |
| 43 | Core::System& system_); | ||
| 38 | ~Display(); | 44 | ~Display(); |
| 39 | 45 | ||
| 40 | /// Gets the unique ID assigned to this display. | 46 | /// Gets the unique ID assigned to this display. |
| @@ -98,9 +104,10 @@ public: | |||
| 98 | private: | 104 | private: |
| 99 | u64 display_id; | 105 | u64 display_id; |
| 100 | std::string name; | 106 | std::string name; |
| 107 | KernelHelpers::ServiceContext& service_context; | ||
| 101 | 108 | ||
| 102 | std::vector<std::shared_ptr<Layer>> layers; | 109 | std::vector<std::shared_ptr<Layer>> layers; |
| 103 | Kernel::KEvent vsync_event; | 110 | Kernel::KEvent* vsync_event{}; |
| 104 | }; | 111 | }; |
| 105 | 112 | ||
| 106 | } // namespace Service::VI | 113 | } // namespace Service::VI |
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 | ||
| 33 | namespace Network { | 36 | namespace Network { |
| @@ -47,11 +50,6 @@ void Finalize() { | |||
| 47 | WSACleanup(); | 50 | WSACleanup(); |
| 48 | } | 51 | } |
| 49 | 52 | ||
| 50 | constexpr 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 | |||
| 55 | sockaddr TranslateFromSockAddrIn(SockAddrIn input) { | 53 | sockaddr TranslateFromSockAddrIn(SockAddrIn input) { |
| 56 | sockaddr_in result; | 54 | sockaddr_in result; |
| 57 | 55 | ||
| @@ -138,12 +136,6 @@ void Initialize() {} | |||
| 138 | 136 | ||
| 139 | void Finalize() {} | 137 | void Finalize() {} |
| 140 | 138 | ||
| 141 | constexpr 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 | |||
| 147 | sockaddr TranslateFromSockAddrIn(SockAddrIn input) { | 139 | sockaddr 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 | ||
| 184 | bool EnableNonBlock(int fd, bool enable) { | 176 | bool 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 | ||
| 197 | Errno TranslateNativeError(int e) { | 189 | Errno 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 | ||
| 234 | int TranslateDomain(Domain domain) { | 230 | int TranslateDomain(Domain domain) { |
| @@ -353,27 +349,29 @@ NetworkInstance::~NetworkInstance() { | |||
| 353 | Finalize(); | 349 | Finalize(); |
| 354 | } | 350 | } |
| 355 | 351 | ||
| 356 | std::pair<IPv4Address, Errno> GetHostIPv4Address() { | 352 | std::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 | ||
| 379 | std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { | 377 | std::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 | |||
| 13 | namespace Network { | 20 | namespace Network { |
| 14 | 21 | ||
| 15 | class Socket; | 22 | class Socket; |
| @@ -92,8 +99,21 @@ public: | |||
| 92 | ~NetworkInstance(); | 99 | ~NetworkInstance(); |
| 93 | }; | 100 | }; |
| 94 | 101 | ||
| 102 | #ifdef _WIN32 | ||
| 103 | constexpr 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 | ||
| 108 | constexpr 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 |
| 97 | std::pair<IPv4Address, Errno> GetHostIPv4Address(); | 117 | std::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..cecc9aa11 --- /dev/null +++ b/src/core/network/network_interface.cpp | |||
| @@ -0,0 +1,203 @@ | |||
| 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 | |||
| 25 | namespace Network { | ||
| 26 | |||
| 27 | #ifdef _WIN32 | ||
| 28 | |||
| 29 | std::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 | adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1); | ||
| 42 | } else { | ||
| 43 | break; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | if (ret == NO_ERROR) { | ||
| 48 | std::vector<NetworkInterface> result; | ||
| 49 | |||
| 50 | for (auto current_address = adapter_addresses.data(); current_address != nullptr; | ||
| 51 | current_address = current_address->Next) { | ||
| 52 | if (current_address->FirstUnicastAddress == nullptr || | ||
| 53 | current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) { | ||
| 54 | continue; | ||
| 55 | } | ||
| 56 | |||
| 57 | if (current_address->OperStatus != IfOperStatusUp) { | ||
| 58 | continue; | ||
| 59 | } | ||
| 60 | |||
| 61 | const auto ip_addr = Common::BitCast<struct sockaddr_in>( | ||
| 62 | *current_address->FirstUnicastAddress->Address.lpSockaddr) | ||
| 63 | .sin_addr; | ||
| 64 | |||
| 65 | ULONG mask = 0; | ||
| 66 | if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength, | ||
| 67 | &mask) != NO_ERROR) { | ||
| 68 | LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask"); | ||
| 69 | continue; | ||
| 70 | } | ||
| 71 | |||
| 72 | struct in_addr gateway = {.S_un{.S_addr{0}}}; | ||
| 73 | if (current_address->FirstGatewayAddress != nullptr && | ||
| 74 | current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) { | ||
| 75 | gateway = Common::BitCast<struct sockaddr_in>( | ||
| 76 | *current_address->FirstGatewayAddress->Address.lpSockaddr) | ||
| 77 | .sin_addr; | ||
| 78 | } | ||
| 79 | |||
| 80 | result.push_back(NetworkInterface{ | ||
| 81 | .name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})}, | ||
| 82 | .ip_address{ip_addr}, | ||
| 83 | .subnet_mask = in_addr{.S_un{.S_addr{mask}}}, | ||
| 84 | .gateway = gateway}); | ||
| 85 | } | ||
| 86 | |||
| 87 | return result; | ||
| 88 | } else { | ||
| 89 | LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses"); | ||
| 90 | return {}; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | #else | ||
| 95 | |||
| 96 | std::vector<NetworkInterface> GetAvailableNetworkInterfaces() { | ||
| 97 | std::vector<NetworkInterface> result; | ||
| 98 | |||
| 99 | struct ifaddrs* ifaddr = nullptr; | ||
| 100 | |||
| 101 | if (getifaddrs(&ifaddr) != 0) { | ||
| 102 | LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}", | ||
| 103 | std::strerror(errno)); | ||
| 104 | return result; | ||
| 105 | } | ||
| 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 | std::uint32_t gateway{0}; | ||
| 121 | std::ifstream file{"/proc/net/route"}; | ||
| 122 | if (file.is_open()) { | ||
| 123 | |||
| 124 | // ignore header | ||
| 125 | file.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); | ||
| 126 | |||
| 127 | bool gateway_found = false; | ||
| 128 | |||
| 129 | for (std::string line; std::getline(file, line);) { | ||
| 130 | std::istringstream iss{line}; | ||
| 131 | |||
| 132 | std::string iface_name{}; | ||
| 133 | iss >> iface_name; | ||
| 134 | if (iface_name != ifa->ifa_name) { | ||
| 135 | continue; | ||
| 136 | } | ||
| 137 | |||
| 138 | iss >> std::hex; | ||
| 139 | |||
| 140 | std::uint32_t dest{0}; | ||
| 141 | iss >> dest; | ||
| 142 | if (dest != 0) { | ||
| 143 | // not the default route | ||
| 144 | continue; | ||
| 145 | } | ||
| 146 | |||
| 147 | iss >> gateway; | ||
| 148 | |||
| 149 | std::uint16_t flags{0}; | ||
| 150 | iss >> flags; | ||
| 151 | |||
| 152 | // flag RTF_GATEWAY (defined in <linux/route.h>) | ||
| 153 | if ((flags & 0x2) == 0) { | ||
| 154 | continue; | ||
| 155 | } | ||
| 156 | |||
| 157 | gateway_found = true; | ||
| 158 | break; | ||
| 159 | } | ||
| 160 | |||
| 161 | if (!gateway_found) { | ||
| 162 | gateway = 0; | ||
| 163 | } | ||
| 164 | } else { | ||
| 165 | LOG_ERROR(Network, "Failed to open \"/proc/net/route\""); | ||
| 166 | } | ||
| 167 | |||
| 168 | result.push_back(NetworkInterface{ | ||
| 169 | .name{ifa->ifa_name}, | ||
| 170 | .ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr}, | ||
| 171 | .subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr}, | ||
| 172 | .gateway{in_addr{.s_addr = gateway}}}); | ||
| 173 | } | ||
| 174 | |||
| 175 | freeifaddrs(ifaddr); | ||
| 176 | |||
| 177 | return result; | ||
| 178 | } | ||
| 179 | |||
| 180 | #endif | ||
| 181 | |||
| 182 | std::optional<NetworkInterface> GetSelectedNetworkInterface() { | ||
| 183 | const std::string& selected_network_interface = Settings::values.network_interface.GetValue(); | ||
| 184 | const auto network_interfaces = Network::GetAvailableNetworkInterfaces(); | ||
| 185 | if (network_interfaces.size() == 0) { | ||
| 186 | LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces"); | ||
| 187 | return {}; | ||
| 188 | } | ||
| 189 | |||
| 190 | const auto res = | ||
| 191 | std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) { | ||
| 192 | return iface.name == selected_network_interface; | ||
| 193 | }); | ||
| 194 | |||
| 195 | if (res != network_interfaces.end()) { | ||
| 196 | return *res; | ||
| 197 | } else { | ||
| 198 | LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface); | ||
| 199 | return {}; | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | } // 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 | |||
| 17 | namespace Network { | ||
| 18 | |||
| 19 | struct NetworkInterface { | ||
| 20 | std::string name; | ||
| 21 | struct in_addr ip_address; | ||
| 22 | struct in_addr subnet_mask; | ||
| 23 | struct in_addr gateway; | ||
| 24 | }; | ||
| 25 | |||
| 26 | std::vector<NetworkInterface> GetAvailableNetworkInterfaces(); | ||
| 27 | std::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 | ||
| 9 | namespace Common { | 10 | namespace Common { |
| 10 | 11 | ||
| 11 | TEST_CASE("ParamPackage", "[common]") { | 12 | TEST_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/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/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/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 | ||
| 1241 | void Config::SaveServiceValues() { | 1242 | void 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*>); | |||
| 103 | void ConfigureDialog::PopulateSelectionList() { | 103 | void 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 |
| 13 | namespace { | 15 | namespace { |
| @@ -35,8 +37,8 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) { | |||
| 35 | } // Anonymous namespace | 37 | } // Anonymous namespace |
| 36 | #endif | 38 | #endif |
| 37 | 39 | ||
| 38 | ConfigureService::ConfigureService(QWidget* parent) | 40 | ConfigureNetwork::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 | ||
| 56 | ConfigureService::~ConfigureService() = default; | 63 | ConfigureNetwork::~ConfigureNetwork() = default; |
| 57 | 64 | ||
| 58 | void ConfigureService::ApplyConfiguration() { | 65 | void 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 | ||
| 62 | void ConfigureService::RetranslateUi() { | 70 | void ConfigureNetwork::RetranslateUi() { |
| 63 | ui->retranslateUi(this); | 71 | ui->retranslateUi(this); |
| 64 | } | 72 | } |
| 65 | 73 | ||
| 66 | void ConfigureService::SetConfiguration() { | 74 | void 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 | ||
| 72 | std::pair<QString, QString> ConfigureService::BCATDownloadEvents() { | 87 | std::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 | ||
| 117 | void ConfigureService::OnBCATImplChanged() { | 132 | void 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 | ||
| 136 | void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) { | 151 | void 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 | ||
| 11 | namespace Ui { | 11 | namespace Ui { |
| 12 | class ConfigureService; | 12 | class ConfigureNetwork; |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | class ConfigureService : public QWidget { | 15 | class ConfigureNetwork : public QWidget { |
| 16 | Q_OBJECT | 16 | Q_OBJECT |
| 17 | 17 | ||
| 18 | public: | 18 | public: |
| 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..1bae1489f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -175,21 +175,6 @@ void GMainWindow::ShowTelemetryCallout() { | |||
| 175 | 175 | ||
| 176 | const int GMainWindow::max_recent_files_item; | 176 | const int GMainWindow::max_recent_files_item; |
| 177 | 177 | ||
| 178 | static 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 | |||
| 193 | static void RemoveCachedContents() { | 178 | static 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,6 @@ 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(); | ||
| 211 | |||
| 212 | LoadTranslation(); | 195 | LoadTranslation(); |
| 213 | 196 | ||
| 214 | setAcceptDrops(true); | 197 | setAcceptDrops(true); |
| @@ -3398,6 +3381,7 @@ void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) { | |||
| 3398 | #endif | 3381 | #endif |
| 3399 | 3382 | ||
| 3400 | int main(int argc, char* argv[]) { | 3383 | int main(int argc, char* argv[]) { |
| 3384 | Common::Log::Initialize(); | ||
| 3401 | Common::DetachedTasks detached_tasks; | 3385 | Common::DetachedTasks detached_tasks; |
| 3402 | MicroProfileOnThreadCreate("Frontend"); | 3386 | MicroProfileOnThreadCreate("Frontend"); |
| 3403 | SCOPE_EXIT({ MicroProfileShutdown(); }); | 3387 | SCOPE_EXIT({ MicroProfileShutdown(); }); |
| @@ -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 | ||
| 77 | static 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 |
| 95 | int main(int argc, char** argv) { | 78 | int 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 | ||