Compare commits

...

78 Commits

Author SHA1 Message Date
ameerj
27f8f3333f vulkan_device: Enable VK_KHR_swapchain_mutable_format if available
Silences validation errors when creating sRGB image views of linear swapchain images
2021-08-29 02:03:36 -04:00
ameerj
3c65c8580f vk_swapchain: Prefer linear swapchain format when presenting sRGB images
Fixes broken sRGB when presenting from a secondary GPU.
2021-08-29 02:03:35 -04:00
Fernando S
3843995ceb Merge pull request #6919 from ameerj/vk-int8-capability
vulkan_device: Add a check for int8 support
2021-08-25 23:46:08 +02:00
Ameer J
de71a4d70d Merge pull request #6894 from FernandoS27/bunneis-left-ear
GPU_MemoryManger: Fix GetSubmappedRange.
2021-08-25 16:50:03 -04:00
bunnei
4654fb96b0 Merge pull request #6917 from ameerj/log-init-fix
logging: Fix log filter during initialization
2021-08-24 18:31:27 -07:00
ameerj
4d535799eb vulkan_device: Add a check for int8 support
Silences validation errors when shaders use int8 without specifying its support to the API
2021-08-24 21:22:41 -04:00
ameerj
84b4ac5729 logging: Fix log filter during initialization
The log filter was being ignored on initialization due to the logging instance being initialized before the config instance, so the log filter was set to its default value.

This fixes that oversight, along with using descriptive exceptions instead of abort() calls.
2021-08-24 01:32:38 -04:00
Ameer J
bed0c3c92a Merge pull request #6878 from BreadFish64/optimize-GetHostThreadID
kernel: Optimize GetHostThreadID
2021-08-24 00:01:13 -04:00
bunnei
7eb4542c1d Merge pull request #6912 from lioncash/plural
CMakeLists: Ensure proper numerusform tags are generated for pluralized translations
2021-08-23 16:40:31 -07:00
bunnei
f65f8b9097 Merge pull request #6869 from yzct12345/shiny-logs-in-the-fireplace
logging: Simplify and make thread-safe
2021-08-22 20:40:18 -07:00
Lioncash
6fef91ce4c CMakeLists: Ensure proper numerusform tags are generated for pluralized translations
If we don't set an explicit source and target language for the base
english translation, then we'll generate an incorrect number of
<numerusform> tags (which Transifex doesn't like).
2021-08-22 11:52:37 -04:00
Ameer J
a428a843cb Merge pull request #6904 from Morph1984/lang-settings-range
settings: Amend language_index maximum setting range
2021-08-21 15:39:55 -04:00
Morph
c4a1d3cbf4 settings: Amend language_index maximum setting range
The maximum is now 17 with the addition of Brazilian Portuguese
2021-08-21 09:43:15 -04:00
Ameer J
bde6b899a1 Merge pull request #6888 from v1993/patch-3
video_core: eliminate constant ternary
2021-08-21 00:16:18 -04:00
bunnei
455e28790a Merge pull request #6877 from MerryMage/dyn-ignore-asserts
dynarmic: Update and enable DYNARMIC_IGNORE_ASSERTS
2021-08-19 15:53:14 -07:00
Mai M
519978ce70 Merge pull request #6887 from v1993/patch-2
SPIR-V: Merge two ifs in EmitGetAttribute
2021-08-19 17:16:37 -04:00
Mai M
0821d95399 Merge pull request #6886 from Morph1984/error-code-64
applet_error: Fix 64-bit error code conversion
2021-08-19 17:15:49 -04:00
Mai M
155b99ee55 Merge pull request #6890 from v1993/patch-5
Replace QPoint with QPointF where applicable
2021-08-19 17:14:48 -04:00
Fernando Sahmkow
ef2066b272 GPU_MemoryManger: Fix GetSubmappedRange. 2021-08-19 22:57:22 +02:00
Valeri
d82fc52cea Replace QPoint with QPointF where applicable
Previously, floats were implicitly cast to integers
2021-08-19 23:16:58 +03:00
Morph
151ab86204 Merge pull request #6889 from v1993/patch-4
qt_software_keyboard: fix copy-paste error
2021-08-19 15:32:07 -04:00
Valeri
40674b8e22 qt_software_keyboard: fix copy-paste error 2021-08-19 22:17:15 +03:00
Valeri
4fd655cb46 video_core: eliminate constant ternary
`via_header_index` is already checked above, so it would never be true in this branch
2021-08-19 21:22:05 +03:00
Morph
9cb376f8c2 applet_error: Fix 64-bit error code conversion 2021-08-19 13:16:48 -04:00
Valeri
beb7305b73 SPIR-V: Merge two ifs in EmitGetAttribute 2021-08-19 20:13:46 +03:00
Mai M
562d2aa3d6 Merge pull request #6885 from v1993/patch-1
Fix crash in logging in CreateStrayLayer
2021-08-19 12:34:34 -04:00
Valeri
ab02addde3 Fix crash in logging in CreateStrayLayer
It was trying to log value of layer_id which is specifically known not to exist, potentially leading to segfault. Log display_id instead.
2021-08-19 19:33:07 +03:00
Morph
aa74aaf38f Merge pull request #6884 from v1993/patch-1
Fix check is thread current in GetThreadContext
2021-08-19 10:39:35 -04:00
Valeri
0b3d12be40 Fix check is thread current in GetThreadContext
Misplaced break made it only check for the first core.
2021-08-19 16:46:30 +03:00
bunnei
aa40084c24 Merge pull request #6832 from bunnei/scheduler-improvements
kernel: Various improvements to scheduler
2021-08-18 15:42:46 -07:00
Fernando S
521e6ac174 Merge pull request #6863 from spholz/fix-lan-play
Fix LAN Play
2021-08-16 17:10:00 +02:00
BreadFish64
14e93f133a kernel: Optimize GetHostThreadID 2021-08-16 07:30:23 -05:00
Sönke Holz
356dbf4d1d network_interface: correct formatting 2021-08-16 12:18:19 +02:00
spholz
dc47b5a5bf network_interface: fix mingw-w64 build 2021-08-16 12:06:35 +02:00
Sönke Holz
70419f7a17 network: retrieve subnet mask and gateway info 2021-08-16 10:32:25 +02:00
Merry
af6290ed12 dynarmic: Update and enable DYNARMIC_IGNORE_ASSERTS 2021-08-15 19:33:02 +01:00
Merry
1770503185 xbyak: Update include path 2021-08-15 19:26:38 +01:00
bunnei
87d63b858a Merge pull request #6861 from yzct12345/const-mempy-is-all-the-speed
decoders: Optimize memcpy for the other functions
2021-08-15 02:38:12 -07:00
bunnei
aef0ca6f0d core: hle: kernel: Disable dispatch count tracking on single core.
- This would have limited value, and would be a mess to handle properly.
2021-08-14 02:14:19 -07:00
yzct12345
001675dced logging: Simplify and make thread-safe
This simplifies the logging system.

This also fixes some lost messages on startup.

The simplification is simple. I removed unused functions and moved most things in the .h to the .cpp. I replaced the unnecessary linked list with its contents laid out as three member variables. Anything that went through the linked list now directly accesses the backends. Generic functions are replaced with those for each specific use case and there aren't many. This change increases coupling but we gain back more KISS and encapsulation.

With those changes it was easy to make it thread-safe. I just removed the mutex and turned a boolean atomic. I was planning to use this thread-safety in my next PR about stacktraces. It was actually async-signal-safety at first but I ended up using a different approach. Anyway getting rid of the linked list is important for that because have the list of backends constantly changing complicates things.
2021-08-13 18:39:45 +00:00
Sönke Holz
068c66672d configuration: fix mingw-w64 build 2021-08-13 12:39:14 +02:00
spholz
deb65a5717 network: don't use reinterpret_cast in GetAvailableNetworkInterfaces 2021-08-13 11:58:34 +02:00
Sönke Holz
e660334a21 network: fix mingw-w64 build
The header "combaseapi.h" of mingw-w64 defines "interface" as "struct".
2021-08-13 11:23:50 +02:00
Sönke Holz
b18e1d031f network: don't use assert to check if no network interfaces are returned 2021-08-13 11:21:34 +02:00
Sönke Holz
0476751ee2 configuration: move network_interface include to source file 2021-08-13 02:48:39 +02:00
Sönke Holz
a0c4c1a23a network: use Common::BitCast instead of std::bit_cast 2021-08-13 01:28:14 +02:00
Sönke Holz
8513e59431 network: narrow down scope of "result" in win32 code for
GetAvailableNetworkInterfaces
2021-08-13 00:37:03 +02:00
Sönke Holz
04ec426201 configuration: use tr instead of QStringLiteral for "None" item in
network interface combobox
2021-08-13 00:34:04 +02:00
Sönke Holz
771de32af1 network: use explicit bool conversions in GetAvailableNetworkInterfaces 2021-08-13 00:31:33 +02:00
Sönke Holz
765e97c347 network: initialize ip_addr in GetHostIPv4Address() 2021-08-13 00:28:44 +02:00
Sönke Holz
acca8aca8c nifm: use operator*() instead of .value() to get value of std::optional 2021-08-13 00:24:33 +02:00
Sönke Holz
970d81abfc nifm: treat a missing host IP address as a non-critical error 2021-08-13 00:21:54 +02:00
spholz
78a8249593 Merge branch 'yuzu-emu:master' into fix-lan-play 2021-08-12 22:27:17 +02:00
Sönke Holz
21743daf38 network: correct formatting in network.cpp and network_interface.cpp 2021-08-12 22:15:48 +02:00
spholz
1e98e73828 configuration: add option to select network interface
This commit renames the "Services" tab to "Network" and adds a combobox that allows the user to select the network interface that yuzu should use. This new setting is now used to get the local IP address in Network::GetHostIPv4Address. This prevents yuzu from selecting the wrong network interface and thus using the wrong IP address. The return type of Network::GetHostIPv4Adress has also been changed.
2021-08-12 21:32:53 +02:00
yzct12345
430255caf8 decoders: Templates allow memcpy optimizations 2021-08-12 04:45:25 +00:00
bunnei
5060a97210 core: hle: kernel: k_thread: Mark KScopedDisableDispatch as nodiscard. 2021-08-07 12:33:31 -07:00
bunnei
9e3d1d865c core: cpu_manager: Use invalid core_id on init and simplify shutdown. 2021-08-07 12:33:07 -07:00
bunnei
99bc49e76e core: hle: service: buffer_queue: Improve management of KEvent. 2021-08-07 12:18:48 -07:00
bunnei
48a3496b93 core: hle: kernel: k_auto_object: Add GetName method.
- Useful purely for debugging.
2021-08-07 12:18:48 -07:00
bunnei
36cf96857e core: hle: service: nvflinger/vi: Improve management of KEvent. 2021-08-07 12:18:47 -07:00
bunnei
5051d3c415 core: hle: kernel: DisableDispatch on suspend threads. 2021-08-07 12:18:47 -07:00
bunnei
1798c3b6b0 core: hle: kernel: k_scheduler: Improve DisableScheduling and EnableScheduling. 2021-08-07 12:18:47 -07:00
bunnei
cbe4e32d38 core: cpu_manager: Use KScopedDisableDispatch. 2021-08-07 12:18:47 -07:00
bunnei
2dfb07388a core: hle: kernel: Use CurrentPhysicalCoreIndex as appropriate. 2021-08-07 12:18:47 -07:00
bunnei
d1c502720d core: hle: kernel: k_scheduler: Remove unnecessary MakeCurrentProcess. 2021-08-07 12:18:47 -07:00
bunnei
77ad64b97d core: hle: kernel: k_scheduler: Improve ScheduleImpl. 2021-08-07 12:18:47 -07:00
bunnei
bedcf19710 core: hle: kernel: k_scheduler: Improve Unload. 2021-08-07 12:18:47 -07:00
bunnei
7569d6774d core: hle: kernel: k_process: DisableDispatch on main thread. 2021-08-07 12:18:47 -07:00
bunnei
f2b0d28983 core: hle: kernel: k_handle_table: Use KScopedDisableDispatch as necessary. 2021-08-07 12:18:47 -07:00
bunnei
01af2f4162 core: hle: kernel: k_thread: Add KScopedDisableDispatch. 2021-08-07 12:18:47 -07:00
bunnei
2b9560428b core: hle: kernel: Ensure idle threads are closed before destroying scheduler. 2021-08-07 12:18:47 -07:00
bunnei
68eee94875 core: hle: kernel: Reflect non-emulated threads as core 3. 2021-08-07 12:18:47 -07:00
bunnei
5ea0d3629a core: cpu_manager: Use jthread. 2021-08-07 12:18:47 -07:00
spholz
33ebe471e8 Merge branch 'yuzu-emu:master' into fix-lan-play 2021-08-07 02:55:19 +02:00
Sönke Holz
ddeb8d854e network: GetAndLogLastError: ignore Errno::AGAIN
If non-blocking sockets are used, they generate a lot of Errno::AGAIN errors when they didn't receive any data. These errors shouldn't be logged.
2021-08-07 02:54:25 +02:00
Sönke Holz
dd5c41b5a6 network: GetCurrentIpConfigInfo: return host IP address
Service::NIFM::IGeneralService::GetCurrentIpConfigInfo currently hardcodes 192.168.1.100 as the IP address, which prevents LAN play from working correctly.
2021-08-07 02:17:02 +02:00
Sönke Holz
652e5e3df0 network: fix fcntl cmds
F_SETFL/F_GETFL are the correct commands to set a socket to be non-blocking
2021-08-06 21:08:31 +02:00
64 changed files with 1170 additions and 699 deletions

View File

@@ -701,7 +701,7 @@ if (APPLE)
elseif (WIN32)
# WSAPoll and SHGetKnownFolderPath (AppData/Roaming) didn't exist before WinNT 6.x (Vista)
add_definitions(-D_WIN32_WINNT=0x0600 -DWINVER=0x0600)
set(PLATFORM_LIBRARIES winmm ws2_32)
set(PLATFORM_LIBRARIES winmm ws2_32 iphlpapi)
if (MINGW)
# PSAPI is the Process Status API
set(PLATFORM_LIBRARIES ${PLATFORM_LIBRARIES} psapi imm32 version)

View File

@@ -7,7 +7,9 @@ include(DownloadExternals)
# xbyak
if (ARCHITECTURE_x86 OR ARCHITECTURE_x86_64)
add_library(xbyak INTERFACE)
target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
endif()
@@ -19,6 +21,7 @@ target_include_directories(catch-single-include INTERFACE catch/single_include)
if (ARCHITECTURE_x86_64)
set(DYNARMIC_TESTS OFF)
set(DYNARMIC_NO_BUNDLED_FMT ON)
set(DYNARMIC_IGNORE_ASSERTS ON CACHE BOOL "" FORCE)
add_subdirectory(dynarmic)
endif()

View File

@@ -2,13 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <atomic>
#include <chrono>
#include <climits>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <exception>
#include <thread>
#include <vector>
@@ -16,28 +13,173 @@
#include <windows.h> // For OutputDebugStringW
#endif
#include "common/assert.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/fs_paths.h"
#include "common/fs/path_util.h"
#include "common/literals.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/settings.h"
#ifdef _WIN32
#include "common/string_util.h"
#endif
#include "common/threadsafe_queue.h"
namespace Common::Log {
namespace {
/**
* Interface for logging backends.
*/
class Backend {
public:
virtual ~Backend() = default;
virtual void Write(const Entry& entry) = 0;
virtual void EnableForStacktrace() = 0;
virtual void Flush() = 0;
};
/**
* Backend that writes to stderr and with color
*/
class ColorConsoleBackend final : public Backend {
public:
explicit ColorConsoleBackend() = default;
~ColorConsoleBackend() override = default;
void Write(const Entry& entry) override {
if (enabled.load(std::memory_order_relaxed)) {
PrintColoredMessage(entry);
}
}
void Flush() override {
// stderr shouldn't be buffered
}
void EnableForStacktrace() override {
enabled = true;
}
void SetEnabled(bool enabled_) {
enabled = enabled_;
}
private:
std::atomic_bool enabled{false};
};
/**
* Backend that writes to a file passed into the constructor
*/
class FileBackend final : public Backend {
public:
explicit FileBackend(const std::filesystem::path& filename) {
auto old_filename = filename;
old_filename += ".old.txt";
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
static_cast<void>(FS::RemoveFile(old_filename));
static_cast<void>(FS::RenameFile(filename, old_filename));
file = std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write,
FS::FileType::TextFile);
}
~FileBackend() override = default;
void Write(const Entry& entry) override {
if (!enabled) {
return;
}
bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
using namespace Common::Literals;
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
const auto write_limit = Settings::values.extended_logging ? 1_GiB : 100_MiB;
const bool write_limit_exceeded = bytes_written > write_limit;
if (entry.log_level >= Level::Error || write_limit_exceeded) {
if (write_limit_exceeded) {
// Stop writing after the write limit is exceeded.
// Don't close the file so we can print a stacktrace if necessary
enabled = false;
}
file->Flush();
}
}
void Flush() override {
file->Flush();
}
void EnableForStacktrace() override {
enabled = true;
bytes_written = 0;
}
private:
std::unique_ptr<FS::IOFile> file;
bool enabled = true;
std::size_t bytes_written = 0;
};
/**
* Backend that writes to Visual Studio's output window
*/
class DebuggerBackend final : public Backend {
public:
explicit DebuggerBackend() = default;
~DebuggerBackend() override = default;
void Write(const Entry& entry) override {
#ifdef _WIN32
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
#endif
}
void Flush() override {}
void EnableForStacktrace() override {}
};
bool initialization_in_progress_suppress_logging = true;
/**
* Static state as a singleton.
*/
class Impl {
public:
static Impl& Instance() {
static Impl backend;
return backend;
if (!instance) {
throw std::runtime_error("Using Logging instance before its initialization");
}
return *instance;
}
static void Initialize() {
if (instance) {
LOG_WARNING(Log, "Reinitializing logging backend");
return;
}
using namespace Common::FS;
const auto& log_dir = GetYuzuPath(YuzuPath::LogDir);
void(CreateDir(log_dir));
Filter filter;
filter.ParseFilterString(Settings::values.log_filter.GetValue());
instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter),
Deleter);
initialization_in_progress_suppress_logging = false;
}
Impl(const Impl&) = delete;
@@ -46,74 +188,54 @@ public:
Impl(Impl&&) = delete;
Impl& operator=(Impl&&) = delete;
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, std::string message) {
message_queue.Push(
CreateEntry(log_class, log_level, filename, line_num, function, std::move(message)));
}
void AddBackend(std::unique_ptr<Backend> backend) {
std::lock_guard lock{writing_mutex};
backends.push_back(std::move(backend));
}
void RemoveBackend(std::string_view backend_name) {
std::lock_guard lock{writing_mutex};
std::erase_if(backends, [&backend_name](const auto& backend) {
return backend_name == backend->GetName();
});
}
const Filter& GetGlobalFilter() const {
return filter;
}
void SetGlobalFilter(const Filter& f) {
filter = f;
}
Backend* GetBackend(std::string_view backend_name) {
const auto it =
std::find_if(backends.begin(), backends.end(),
[&backend_name](const auto& i) { return backend_name == i->GetName(); });
if (it == backends.end())
return nullptr;
return it->get();
void SetColorConsoleBackendEnabled(bool enabled) {
color_console_backend.SetEnabled(enabled);
}
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, std::string message) {
if (!filter.CheckMessage(log_class, log_level))
return;
const Entry& entry =
CreateEntry(log_class, log_level, filename, line_num, function, std::move(message));
message_queue.Push(entry);
}
private:
Impl() {
backend_thread = std::thread([&] {
Entry entry;
auto write_logs = [&](Entry& e) {
std::lock_guard lock{writing_mutex};
for (const auto& backend : backends) {
backend->Write(e);
}
};
while (true) {
entry = message_queue.PopWait();
if (entry.final_entry) {
break;
}
write_logs(entry);
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
// case where a system is repeatedly spamming logs even on close.
const int MAX_LOGS_TO_WRITE = filter.IsDebug() ? INT_MAX : 100;
int logs_written = 0;
while (logs_written++ < MAX_LOGS_TO_WRITE && message_queue.Pop(entry)) {
write_logs(entry);
}
});
}
Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
: filter{filter_}, file_backend{file_backend_filename}, backend_thread{std::thread([this] {
Common::SetCurrentThreadName("yuzu:Log");
Entry entry;
const auto write_logs = [this, &entry]() {
ForEachBackend([&entry](Backend& backend) { backend.Write(entry); });
};
while (true) {
entry = message_queue.PopWait();
if (entry.final_entry) {
break;
}
write_logs();
}
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
// case where a system is repeatedly spamming logs even on close.
int max_logs_to_write = filter.IsDebug() ? INT_MAX : 100;
while (max_logs_to_write-- && message_queue.Pop(entry)) {
write_logs();
}
})} {}
~Impl() {
Entry entry;
entry.final_entry = true;
message_queue.Push(entry);
StopBackendThread();
}
void StopBackendThread() {
Entry stop_entry{};
stop_entry.final_entry = true;
message_queue.Push(stop_entry);
backend_thread.join();
}
@@ -135,100 +257,51 @@ private:
};
}
std::mutex writing_mutex;
std::thread backend_thread;
std::vector<std::unique_ptr<Backend>> backends;
MPSCQueue<Entry> message_queue;
void ForEachBackend(auto lambda) {
lambda(static_cast<Backend&>(debugger_backend));
lambda(static_cast<Backend&>(color_console_backend));
lambda(static_cast<Backend&>(file_backend));
}
static void Deleter(Impl* ptr) {
delete ptr;
}
static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
Filter filter;
DebuggerBackend debugger_backend{};
ColorConsoleBackend color_console_backend{};
FileBackend file_backend;
std::thread backend_thread;
MPSCQueue<Entry> message_queue{};
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
};
} // namespace
ConsoleBackend::~ConsoleBackend() = default;
void ConsoleBackend::Write(const Entry& entry) {
PrintMessage(entry);
void Initialize() {
Impl::Initialize();
}
ColorConsoleBackend::~ColorConsoleBackend() = default;
void ColorConsoleBackend::Write(const Entry& entry) {
PrintColoredMessage(entry);
}
FileBackend::FileBackend(const std::filesystem::path& filename) {
auto old_filename = filename;
old_filename += ".old.txt";
// Existence checks are done within the functions themselves.
// We don't particularly care if these succeed or not.
FS::RemoveFile(old_filename);
void(FS::RenameFile(filename, old_filename));
file =
std::make_unique<FS::IOFile>(filename, FS::FileAccessMode::Write, FS::FileType::TextFile);
}
FileBackend::~FileBackend() = default;
void FileBackend::Write(const Entry& entry) {
if (!file->IsOpen()) {
return;
}
using namespace Common::Literals;
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
constexpr std::size_t MAX_BYTES_WRITTEN = 100_MiB;
constexpr std::size_t MAX_BYTES_WRITTEN_EXTENDED = 1_GiB;
const bool write_limit_exceeded =
bytes_written > MAX_BYTES_WRITTEN_EXTENDED ||
(bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging);
// Close the file after the write limit is exceeded.
if (write_limit_exceeded) {
file->Close();
return;
}
bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) {
file->Flush();
}
}
DebuggerBackend::~DebuggerBackend() = default;
void DebuggerBackend::Write(const Entry& entry) {
#ifdef _WIN32
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
#endif
void DisableLoggingInTests() {
initialization_in_progress_suppress_logging = true;
}
void SetGlobalFilter(const Filter& filter) {
Impl::Instance().SetGlobalFilter(filter);
}
void AddBackend(std::unique_ptr<Backend> backend) {
Impl::Instance().AddBackend(std::move(backend));
}
void RemoveBackend(std::string_view backend_name) {
Impl::Instance().RemoveBackend(backend_name);
}
Backend* GetBackend(std::string_view backend_name) {
return Impl::Instance().GetBackend(backend_name);
void SetColorConsoleBackendEnabled(bool enabled) {
Impl::Instance().SetColorConsoleBackendEnabled(enabled);
}
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format,
const fmt::format_args& args) {
auto& instance = Impl::Instance();
const auto& filter = instance.GetGlobalFilter();
if (!filter.CheckMessage(log_class, log_level))
return;
instance.PushEntry(log_class, log_level, filename, line_num, function,
fmt::vformat(format, args));
if (!initialization_in_progress_suppress_logging) {
Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function,
fmt::vformat(format, args));
}
}
} // namespace Common::Log

View File

@@ -5,120 +5,21 @@
#pragma once
#include <filesystem>
#include <memory>
#include <string>
#include <string_view>
#include "common/logging/filter.h"
#include "common/logging/log.h"
namespace Common::FS {
class IOFile;
}
namespace Common::Log {
class Filter;
/**
* Interface for logging backends. As loggers can be created and removed at runtime, this can be
* used by a frontend for adding a custom logging backend as needed
*/
class Backend {
public:
virtual ~Backend() = default;
/// Initializes the logging system. This should be the first thing called in main.
void Initialize();
virtual void SetFilter(const Filter& new_filter) {
filter = new_filter;
}
virtual const char* GetName() const = 0;
virtual void Write(const Entry& entry) = 0;
private:
Filter filter;
};
void DisableLoggingInTests();
/**
* Backend that writes to stderr without any color commands
*/
class ConsoleBackend : public Backend {
public:
~ConsoleBackend() override;
static const char* Name() {
return "console";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
/**
* Backend that writes to stderr and with color
*/
class ColorConsoleBackend : public Backend {
public:
~ColorConsoleBackend() override;
static const char* Name() {
return "color_console";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
/**
* Backend that writes to a file passed into the constructor
*/
class FileBackend : public Backend {
public:
explicit FileBackend(const std::filesystem::path& filename);
~FileBackend() override;
static const char* Name() {
return "file";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
private:
std::unique_ptr<FS::IOFile> file;
std::size_t bytes_written = 0;
};
/**
* Backend that writes to Visual Studio's output window
*/
class DebuggerBackend : public Backend {
public:
~DebuggerBackend() override;
static const char* Name() {
return "debugger";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
void AddBackend(std::unique_ptr<Backend> backend);
void RemoveBackend(std::string_view backend_name);
Backend* GetBackend(std::string_view backend_name);
/**
* The global filter will prevent any messages from even being processed if they are filtered. Each
* backend can have a filter, but if the level is lower than the global filter, the backend will
* never get the message
* The global filter will prevent any messages from even being processed if they are filtered.
*/
void SetGlobalFilter(const Filter& filter);
} // namespace Common::Log
void SetColorConsoleBackendEnabled(bool enabled);
} // namespace Common::Log

View File

@@ -489,7 +489,7 @@ struct Values {
std::chrono::seconds custom_rtc_differential;
BasicSetting<s32> current_user{0, "current_user"};
RangedSetting<s32> language_index{1, 0, 16, "language_index"};
RangedSetting<s32> language_index{1, 0, 17, "language_index"};
RangedSetting<s32> region_index{1, 0, 6, "region_index"};
RangedSetting<s32> time_zone_index{0, 0, 45, "time_zone_index"};
RangedSetting<s32> sound_index{1, 0, 2, "sound_index"};
@@ -558,9 +558,10 @@ struct Values {
BasicSetting<std::string> log_filter{"*:Info", "log_filter"};
BasicSetting<bool> use_dev_keys{false, "use_dev_keys"};
// Services
// Network
BasicSetting<std::string> bcat_backend{"none", "bcat_backend"};
BasicSetting<bool> bcat_boxcat_local{false, "bcat_boxcat_local"};
BasicSetting<std::string> network_interface{std::string(), "network_interface"};
// WebService
BasicSetting<bool> enable_telemetry{true, "enable_telemetry"};

View File

@@ -6,7 +6,7 @@
#include <bitset>
#include <initializer_list>
#include <xbyak.h>
#include <xbyak/xbyak.h>
#include "common/assert.h"
namespace Common::X64 {

View File

@@ -5,7 +5,7 @@
#pragma once
#include <type_traits>
#include <xbyak.h>
#include <xbyak/xbyak.h>
#include "common/x64/xbyak_abi.h"
namespace Common::X64 {

View File

@@ -636,6 +636,8 @@ add_library(core STATIC
memory.h
network/network.cpp
network/network.h
network/network_interface.cpp
network/network_interface.h
network/sockets.h
perf_stats.cpp
perf_stats.h

View File

@@ -4,6 +4,7 @@
#include <array>
#include <atomic>
#include <exception>
#include <memory>
#include <utility>
@@ -84,8 +85,6 @@ FileSys::StorageId GetStorageIdForFrontendSlot(
} // Anonymous namespace
/*static*/ System System::s_instance;
FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
const std::string& path) {
// To account for split 00+01+etc files.
@@ -425,6 +424,20 @@ struct System::Impl {
System::System() : impl{std::make_unique<Impl>(*this)} {}
System::~System() = default;
System& System::GetInstance() {
if (!s_instance) {
throw std::runtime_error("Using System instance before its initialization");
}
return *s_instance;
}
void System::InitializeGlobalInstance() {
if (s_instance) {
throw std::runtime_error("Reinitializing Global System instance.");
}
s_instance = std::unique_ptr<System>(new System);
}
CpuManager& System::GetCpuManager() {
return impl->cpu_manager;
}
@@ -494,12 +507,6 @@ const ARM_Interface& System::CurrentArmInterface() const {
return impl->kernel.CurrentPhysicalCore().ArmInterface();
}
std::size_t System::CurrentCoreIndex() const {
std::size_t core = impl->kernel.GetCurrentHostThreadID();
ASSERT(core < Core::Hardware::NUM_CPU_CORES);
return core;
}
Kernel::PhysicalCore& System::CurrentPhysicalCore() {
return impl->kernel.CurrentPhysicalCore();
}

View File

@@ -120,9 +120,9 @@ public:
* Gets the instance of the System singleton class.
* @returns Reference to the instance of the System singleton class.
*/
[[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() {
return s_instance;
}
[[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance();
static void InitializeGlobalInstance();
/// Enumeration representing the return values of the System Initialize and Load process.
enum class ResultStatus : u32 {
@@ -205,9 +205,6 @@ public:
/// Gets an ARM interface to the CPU core that is currently running
[[nodiscard]] const ARM_Interface& CurrentArmInterface() const;
/// Gets the index of the currently running CPU core
[[nodiscard]] std::size_t CurrentCoreIndex() const;
/// Gets the physical core for the CPU core that is currently running
[[nodiscard]] Kernel::PhysicalCore& CurrentPhysicalCore();
@@ -396,7 +393,7 @@ private:
struct Impl;
std::unique_ptr<Impl> impl;
static System s_instance;
inline static std::unique_ptr<System> s_instance{};
};
} // namespace Core

View File

@@ -21,34 +21,25 @@ namespace Core {
CpuManager::CpuManager(System& system_) : system{system_} {}
CpuManager::~CpuManager() = default;
void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
cpu_manager.RunThread(core);
void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
std::size_t core) {
cpu_manager.RunThread(stop_token, core);
}
void CpuManager::Initialize() {
running_mode = true;
if (is_multicore) {
for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
core_data[core].host_thread =
std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
}
} else {
core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0);
core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
}
}
void CpuManager::Shutdown() {
running_mode = false;
Pause(false);
if (is_multicore) {
for (auto& data : core_data) {
data.host_thread->join();
data.host_thread.reset();
}
} else {
core_data[0].host_thread->join();
core_data[0].host_thread.reset();
}
}
std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
@@ -127,17 +118,18 @@ void CpuManager::MultiCoreRunGuestLoop() {
physical_core = &kernel.CurrentPhysicalCore();
}
system.ExitDynarmicProfile();
physical_core->ArmInterface().ClearExclusiveState();
kernel.CurrentScheduler()->RescheduleCurrentCore();
{
Kernel::KScopedDisableDispatch dd(kernel);
physical_core->ArmInterface().ClearExclusiveState();
}
}
}
void CpuManager::MultiCoreRunIdleThread() {
auto& kernel = system.Kernel();
while (true) {
auto& physical_core = kernel.CurrentPhysicalCore();
physical_core.Idle();
kernel.CurrentScheduler()->RescheduleCurrentCore();
Kernel::KScopedDisableDispatch dd(kernel);
kernel.CurrentPhysicalCore().Idle();
}
}
@@ -145,12 +137,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
auto& kernel = system.Kernel();
kernel.CurrentScheduler()->OnThreadStart();
while (true) {
auto core = kernel.GetCurrentHostThreadID();
auto core = kernel.CurrentPhysicalCoreIndex();
auto& scheduler = *kernel.CurrentScheduler();
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID());
ASSERT(core == kernel.CurrentPhysicalCoreIndex());
scheduler.RescheduleCurrentCore();
}
}
@@ -317,7 +309,7 @@ void CpuManager::Pause(bool paused) {
}
}
void CpuManager::RunThread(std::size_t core) {
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
/// Initialization
system.RegisterCoreThread(core);
std::string name;
@@ -356,8 +348,8 @@ void CpuManager::RunThread(std::size_t core) {
sc_sync_first_use = false;
}
// Abort if emulation was killed before the session really starts
if (!system.IsPoweredOn()) {
// Emulation was stopped
if (stop_token.stop_requested()) {
return;
}

View File

@@ -78,9 +78,9 @@ private:
void SingleCoreRunSuspendThread();
void SingleCorePause(bool paused);
static void ThreadStart(CpuManager& cpu_manager, std::size_t core);
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
void RunThread(std::size_t core);
void RunThread(std::stop_token stop_token, std::size_t core);
struct CoreData {
std::shared_ptr<Common::Fiber> host_context;
@@ -89,7 +89,7 @@ private:
std::atomic<bool> is_running;
std::atomic<bool> is_paused;
std::atomic<bool> initialized;
std::unique_ptr<std::thread> host_thread;
std::jthread host_thread;
};
std::atomic<bool> running_mode{};

View File

@@ -28,7 +28,7 @@ bool ReadFromUser(Core::System& system, s32* out, VAddr address) {
bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 value) {
auto& monitor = system.Monitor();
const auto current_core = system.CurrentCoreIndex();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.
@@ -58,7 +58,7 @@ bool DecrementIfLessThan(Core::System& system, s32* out, VAddr address, s32 valu
bool UpdateIfEqual(Core::System& system, s32* out, VAddr address, s32 value, s32 new_value) {
auto& monitor = system.Monitor();
const auto current_core = system.CurrentCoreIndex();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
// TODO(bunnei): We should call CanAccessAtomic(..) here.

View File

@@ -170,6 +170,10 @@ public:
}
}
const std::string& GetName() const {
return name;
}
private:
void RegisterWithKernel();
void UnregisterWithKernel();

View File

@@ -35,7 +35,7 @@ bool WriteToUser(Core::System& system, VAddr address, const u32* p) {
bool UpdateLockAtomic(Core::System& system, u32* out, VAddr address, u32 if_zero,
u32 new_orr_mask) {
auto& monitor = system.Monitor();
const auto current_core = system.CurrentCoreIndex();
const auto current_core = system.Kernel().CurrentPhysicalCoreIndex();
// Load the value from the address.
const auto expected = monitor.ExclusiveRead32(current_core, address);

View File

@@ -13,6 +13,7 @@ ResultCode KHandleTable::Finalize() {
// Get the table and clear our record of it.
u16 saved_table_size = 0;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
std::swap(m_table_size, saved_table_size);
@@ -43,6 +44,7 @@ bool KHandleTable::Remove(Handle handle) {
// Find the object and free the entry.
KAutoObject* obj = nullptr;
{
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if (this->IsValidHandle(handle)) {
@@ -61,6 +63,7 @@ bool KHandleTable::Remove(Handle handle) {
}
ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@@ -83,6 +86,7 @@ ResultCode KHandleTable::Add(Handle* out_handle, KAutoObject* obj, u16 type) {
}
ResultCode KHandleTable::Reserve(Handle* out_handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Never exceed our capacity.
@@ -93,6 +97,7 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
}
void KHandleTable::Unreserve(Handle handle) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.
@@ -111,6 +116,7 @@ void KHandleTable::Unreserve(Handle handle) {
}
void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
// Unpack the handle.

View File

@@ -69,6 +69,7 @@ public:
template <typename T = KAutoObject>
KScopedAutoObject<T> GetObjectWithoutPseudoHandle(Handle handle) const {
// Lock and look up in table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
if constexpr (std::is_same_v<T, KAutoObject>) {
@@ -123,6 +124,7 @@ public:
size_t num_opened;
{
// Lock the table.
KScopedDisableDispatch dd(kernel);
KScopedSpinLock lk(m_lock);
for (num_opened = 0; num_opened < num_handles; num_opened++) {
// Get the current handle.

View File

@@ -59,6 +59,7 @@ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority
thread->GetContext64().cpu_registers[0] = 0;
thread->GetContext32().cpu_registers[1] = thread_handle;
thread->GetContext64().cpu_registers[1] = thread_handle;
thread->DisableDispatch();
auto& kernel = system.Kernel();
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires

View File

@@ -376,20 +376,18 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
}
void KScheduler::DisableScheduling(KernelCore& kernel) {
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0);
scheduler->GetCurrentThread()->DisableDispatch();
}
ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
GetCurrentThreadPointer(kernel)->DisableDispatch();
}
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
scheduler->GetCurrentThread()->EnableDispatch();
}
ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 1);
if (GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() > 1) {
GetCurrentThreadPointer(kernel)->EnableDispatch();
} else {
RescheduleCores(kernel, cores_needing_scheduling);
}
RescheduleCores(kernel, cores_needing_scheduling);
}
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@@ -617,13 +615,17 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
state.highest_priority_thread = nullptr;
}
KScheduler::~KScheduler() {
void KScheduler::Finalize() {
if (idle_thread) {
idle_thread->Close();
idle_thread = nullptr;
}
}
KScheduler::~KScheduler() {
ASSERT(!idle_thread);
}
KThread* KScheduler::GetCurrentThread() const {
if (auto result = current_thread.load(); result) {
return result;
@@ -642,10 +644,12 @@ void KScheduler::RescheduleCurrentCore() {
if (phys_core.IsInterrupted()) {
phys_core.ClearInterrupt();
}
guard.Lock();
if (state.needs_scheduling.load()) {
Schedule();
} else {
GetCurrentThread()->EnableDispatch();
guard.Unlock();
}
}
@@ -655,26 +659,33 @@ void KScheduler::OnThreadStart() {
}
void KScheduler::Unload(KThread* thread) {
ASSERT(thread);
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
if (thread) {
if (thread->IsCallingSvc()) {
thread->ClearIsCallingSvc();
}
if (!thread->IsTerminationRequested()) {
prev_thread = thread;
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
} else {
prev_thread = nullptr;
}
thread->context_guard.Unlock();
if (thread->IsCallingSvc()) {
thread->ClearIsCallingSvc();
}
auto& physical_core = system.Kernel().PhysicalCore(core_id);
if (!physical_core.IsInitialized()) {
return;
}
Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified.
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
prev_thread = thread;
} else {
prev_thread = nullptr;
}
thread->context_guard.Unlock();
}
void KScheduler::Reload(KThread* thread) {
@@ -683,11 +694,6 @@ void KScheduler::Reload(KThread* thread) {
if (thread) {
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
auto* const thread_owner_process = thread->GetOwnerProcess();
if (thread_owner_process != nullptr) {
system.Kernel().MakeCurrentProcess(thread_owner_process);
}
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64());
@@ -705,7 +711,7 @@ void KScheduler::SwitchContextStep2() {
}
void KScheduler::ScheduleImpl() {
KThread* previous_thread = current_thread.load();
KThread* previous_thread = GetCurrentThread();
KThread* next_thread = state.highest_priority_thread;
state.needs_scheduling = false;
@@ -717,10 +723,15 @@ void KScheduler::ScheduleImpl() {
// If we're not actually switching thread, there's nothing to do.
if (next_thread == current_thread.load()) {
previous_thread->EnableDispatch();
guard.Unlock();
return;
}
if (next_thread->GetCurrentCore() != core_id) {
next_thread->SetCurrentCore(core_id);
}
current_thread.store(next_thread);
KProcess* const previous_process = system.Kernel().CurrentProcess();
@@ -731,11 +742,7 @@ void KScheduler::ScheduleImpl() {
Unload(previous_thread);
std::shared_ptr<Common::Fiber>* old_context;
if (previous_thread != nullptr) {
old_context = &previous_thread->GetHostContext();
} else {
old_context = &idle_thread->GetHostContext();
}
old_context = &previous_thread->GetHostContext();
guard.Unlock();
Common::Fiber::YieldTo(*old_context, *switch_fiber);

View File

@@ -33,6 +33,8 @@ public:
explicit KScheduler(Core::System& system_, s32 core_id_);
~KScheduler();
void Finalize();
/// Reschedules to the next available thread (call after current thread is suspended)
void RescheduleCurrentCore();

View File

@@ -14,6 +14,7 @@
#include "common/fiber.h"
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h"
#include "common/thread_queue_list.h"
#include "core/core.h"
#include "core/cpu_manager.h"
@@ -188,7 +189,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s
// Setup the stack parameters.
StackParameters& sp = GetStackParameters();
sp.cur_thread = this;
sp.disable_count = 1;
sp.disable_count = 0;
SetInExceptionHandler();
// Set thread ID.
@@ -215,9 +216,10 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint
// Initialize the thread.
R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type));
// Initialize host context.
// Initialize emulation parameters.
thread->host_context =
std::make_shared<Common::Fiber>(std::move(init_func), init_func_parameter);
thread->is_single_core = !Settings::values.use_multi_core.GetValue();
return ResultSuccess;
}
@@ -970,6 +972,9 @@ ResultCode KThread::Run() {
// Set our state and finish.
SetState(ThreadState::Runnable);
DisableDispatch();
return ResultSuccess;
}
}
@@ -1054,4 +1059,16 @@ s32 GetCurrentCoreId(KernelCore& kernel) {
return GetCurrentThread(kernel).GetCurrentCore();
}
KScopedDisableDispatch::~KScopedDisableDispatch() {
if (GetCurrentThread(kernel).GetDisableDispatchCount() <= 1) {
auto scheduler = kernel.CurrentScheduler();
if (scheduler) {
scheduler->RescheduleCurrentCore();
}
} else {
GetCurrentThread(kernel).EnableDispatch();
}
}
} // namespace Kernel

View File

@@ -450,16 +450,39 @@ public:
sleeping_queue = q;
}
[[nodiscard]] bool IsKernelThread() const {
return GetActiveCore() == 3;
}
[[nodiscard]] bool IsDispatchTrackingDisabled() const {
return is_single_core || IsKernelThread();
}
[[nodiscard]] s32 GetDisableDispatchCount() const {
if (IsDispatchTrackingDisabled()) {
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
return 1;
}
return this->GetStackParameters().disable_count;
}
void DisableDispatch() {
if (IsDispatchTrackingDisabled()) {
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
return;
}
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
this->GetStackParameters().disable_count++;
}
void EnableDispatch() {
if (IsDispatchTrackingDisabled()) {
// TODO(bunnei): Until kernel threads are emulated, we cannot enable/disable dispatch.
return;
}
ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
this->GetStackParameters().disable_count--;
}
@@ -708,6 +731,7 @@ private:
// For emulation
std::shared_ptr<Common::Fiber> host_context{};
bool is_single_core{};
// For debugging
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
@@ -752,4 +776,16 @@ public:
}
};
class KScopedDisableDispatch {
public:
[[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
GetCurrentThread(kernel).DisableDispatch();
}
~KScopedDisableDispatch();
private:
KernelCore& kernel;
};
} // namespace Kernel

View File

@@ -85,8 +85,9 @@ struct KernelCore::Impl {
}
void InitializeCores() {
for (auto& core : cores) {
core.Initialize(current_process->Is64BitProcess());
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
cores[core_id].Initialize(current_process->Is64BitProcess());
system.Memory().SetCurrentPageTable(*current_process, core_id);
}
}
@@ -131,15 +132,6 @@ struct KernelCore::Impl {
next_user_process_id = KProcess::ProcessIDMin;
next_thread_id = 1;
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
if (suspend_threads[core_id]) {
suspend_threads[core_id]->Close();
suspend_threads[core_id] = nullptr;
}
schedulers[core_id].reset();
}
cores.clear();
global_handle_table->Finalize();
@@ -167,6 +159,16 @@ struct KernelCore::Impl {
CleanupObject(time_shared_mem);
CleanupObject(system_resource_limit);
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
if (suspend_threads[core_id]) {
suspend_threads[core_id]->Close();
suspend_threads[core_id] = nullptr;
}
schedulers[core_id]->Finalize();
schedulers[core_id].reset();
}
// Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
@@ -257,30 +259,25 @@ struct KernelCore::Impl {
void MakeCurrentProcess(KProcess* process) {
current_process = process;
if (process == nullptr) {
return;
}
const u32 core_id = GetCurrentHostThreadID();
if (core_id < Core::Hardware::NUM_CPU_CORES) {
system.Memory().SetCurrentPageTable(*process, core_id);
}
}
/// Creates a new host thread ID, should only be called by GetHostThreadId
u32 AllocateHostThreadId(std::optional<std::size_t> core_id) {
if (core_id) {
// The first for slots are reserved for CPU core threads
ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES);
return static_cast<u32>(*core_id);
} else {
return next_host_thread_id++;
static inline thread_local u32 host_thread_id = UINT32_MAX;
/// Gets the host thread ID for the caller, allocating a new one if this is the first time
u32 GetHostThreadId(std::size_t core_id) {
if (host_thread_id == UINT32_MAX) {
// The first four slots are reserved for CPU core threads
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
host_thread_id = static_cast<u32>(core_id);
}
return host_thread_id;
}
/// Gets the host thread ID for the caller, allocating a new one if this is the first time
u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) {
const thread_local auto host_thread_id{AllocateHostThreadId(core_id)};
u32 GetHostThreadId() {
if (host_thread_id == UINT32_MAX) {
host_thread_id = next_host_thread_id++;
}
return host_thread_id;
}
@@ -824,16 +821,20 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
return impl->cores[id];
}
size_t KernelCore::CurrentPhysicalCoreIndex() const {
const u32 core_id = impl->GetCurrentHostThreadID();
if (core_id >= Core::Hardware::NUM_CPU_CORES) {
return Core::Hardware::NUM_CPU_CORES - 1;
}
return core_id;
}
Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
u32 core_id = impl->GetCurrentHostThreadID();
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
return impl->cores[core_id];
return impl->cores[CurrentPhysicalCoreIndex()];
}
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
u32 core_id = impl->GetCurrentHostThreadID();
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
return impl->cores[core_id];
return impl->cores[CurrentPhysicalCoreIndex()];
}
Kernel::KScheduler* KernelCore::CurrentScheduler() {
@@ -1026,6 +1027,9 @@ void KernelCore::Suspend(bool in_suspention) {
impl->suspend_threads[core_id]->SetState(state);
impl->suspend_threads[core_id]->SetWaitReasonForDebugging(
ThreadWaitReasonForDebugging::Suspended);
if (!should_suspend) {
impl->suspend_threads[core_id]->DisableDispatch();
}
}
}
}
@@ -1040,13 +1044,11 @@ void KernelCore::ExceptionalExit() {
}
void KernelCore::EnterSVCProfile() {
std::size_t core = impl->GetCurrentHostThreadID();
impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
}
void KernelCore::ExitSVCProfile() {
std::size_t core = impl->GetCurrentHostThreadID();
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
}
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {

View File

@@ -146,6 +146,9 @@ public:
/// Gets the an instance of the respective physical CPU core.
const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
/// Gets the current physical core index for the running host thread.
std::size_t CurrentPhysicalCoreIndex() const;
/// Gets the sole instance of the Scheduler at the current running core.
Kernel::KScheduler* CurrentScheduler();

View File

@@ -877,7 +877,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, Handle
const u64 thread_ticks = current_thread->GetCpuTime();
out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
} else if (same_thread && info_sub_id == system.Kernel().CurrentPhysicalCoreIndex()) {
out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
}
@@ -1078,8 +1078,8 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) {
if (thread.GetPointerUnsafe() == kernel.Scheduler(i).GetCurrentThread()) {
current = true;
break;
}
break;
}
// If the thread is current, retry until it isn't.

View File

@@ -16,6 +16,30 @@
namespace Service::AM::Applets {
struct ErrorCode {
u32 error_category{};
u32 error_number{};
static constexpr ErrorCode FromU64(u64 error_code) {
return {
.error_category{static_cast<u32>(error_code >> 32)},
.error_number{static_cast<u32>(error_code & 0xFFFFFFFF)},
};
}
static constexpr ErrorCode FromResultCode(ResultCode result) {
return {
.error_category{2000 + static_cast<u32>(result.module.Value())},
.error_number{result.description.Value()},
};
}
constexpr ResultCode ToResultCode() const {
return ResultCode{static_cast<ErrorModule>(error_category - 2000), error_number};
}
};
static_assert(sizeof(ErrorCode) == 0x8, "ErrorCode has incorrect size.");
#pragma pack(push, 4)
struct ShowError {
u8 mode;
@@ -76,12 +100,7 @@ void CopyArgumentData(const std::vector<u8>& data, T& variable) {
}
ResultCode Decode64BitError(u64 error) {
const auto description = (error >> 32) & 0x1FFF;
auto module = error & 0x3FF;
if (module >= 2000)
module -= 2000;
module &= 0x1FF;
return {static_cast<ErrorModule>(module), static_cast<u32>(description)};
return ErrorCode::FromU64(error).ToResultCode();
}
} // Anonymous namespace

View File

@@ -11,6 +11,7 @@
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/service.h"
#include "core/network/network.h"
#include "core/network/network_interface.h"
namespace Service::NIFM {
@@ -179,10 +180,10 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
if (Settings::values.bcat_backend.GetValue() == "none") {
rb.PushEnum(RequestState::NotSubmitted);
} else {
if (Network::GetHostIPv4Address().has_value()) {
rb.PushEnum(RequestState::Connected);
} else {
rb.PushEnum(RequestState::NotSubmitted);
}
}
@@ -322,12 +323,15 @@ private:
void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
const auto [ipv4, error] = Network::GetHostIPv4Address();
UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS);
auto ipv4 = Network::GetHostIPv4Address();
if (!ipv4) {
LOG_ERROR(Service_NIFM, "Couldn't get host IPv4 address, defaulting to 0.0.0.0");
ipv4.emplace(Network::IPv4Address{0, 0, 0, 0});
}
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.PushRaw(ipv4);
rb.PushRaw(*ipv4);
}
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
@@ -354,10 +358,10 @@ private:
static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
"IpConfigInfo has incorrect size.");
const IpConfigInfo ip_config_info{
IpConfigInfo ip_config_info{
.ip_address_setting{
.is_automatic{true},
.current_address{192, 168, 1, 100},
.current_address{0, 0, 0, 0},
.subnet_mask{255, 255, 255, 0},
.gateway{192, 168, 1, 1},
},
@@ -368,6 +372,19 @@ private:
},
};
const auto iface = Network::GetSelectedNetworkInterface();
if (iface) {
ip_config_info.ip_address_setting =
IpAddressSetting{.is_automatic{true},
.current_address{Network::TranslateIPv4(iface->ip_address)},
.subnet_mask{Network::TranslateIPv4(iface->subnet_mask)},
.gateway{Network::TranslateIPv4(iface->gateway)}};
} else {
LOG_ERROR(Service_NIFM,
"Couldn't get host network configuration info, using default values");
}
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
rb.Push(ResultSuccess);
rb.PushRaw<IpConfigInfo>(ip_config_info);
@@ -384,10 +401,10 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
if (Settings::values.bcat_backend.GetValue() == "none") {
rb.Push<u8>(0);
} else {
if (Network::GetHostIPv4Address().has_value()) {
rb.Push<u8>(1);
} else {
rb.Push<u8>(0);
}
}
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
@@ -395,10 +412,10 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
if (Settings::values.bcat_backend.GetValue() == "none") {
rb.Push<u8>(0);
} else {
if (Network::GetHostIPv4Address().has_value()) {
rb.Push<u8>(1);
} else {
rb.Push<u8>(0);
}
}
};

View File

@@ -9,17 +9,20 @@
#include "core/core.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
namespace Service::NVFlinger {
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_)
: id(id_), layer_id(layer_id_), buffer_wait_event{kernel} {
Kernel::KAutoObject::Create(std::addressof(buffer_wait_event));
buffer_wait_event.Initialize("BufferQueue:WaitEvent");
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
KernelHelpers::ServiceContext& service_context_)
: id(id_), layer_id(layer_id_), service_context{service_context_} {
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
}
BufferQueue::~BufferQueue() = default;
BufferQueue::~BufferQueue() {
service_context.CloseEvent(buffer_wait_event);
}
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
ASSERT(slot < buffer_slots);
@@ -41,7 +44,7 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
.multi_fence = {},
};
buffer_wait_event.GetWritableEvent().Signal();
buffer_wait_event->GetWritableEvent().Signal();
}
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
}
free_buffers_condition.notify_one();
buffer_wait_event.GetWritableEvent().Signal();
buffer_wait_event->GetWritableEvent().Signal();
}
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
@@ -154,7 +157,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
}
free_buffers_condition.notify_one();
buffer_wait_event.GetWritableEvent().Signal();
buffer_wait_event->GetWritableEvent().Signal();
}
void BufferQueue::Connect() {
@@ -169,7 +172,7 @@ void BufferQueue::Disconnect() {
std::unique_lock lock{queue_sequence_mutex};
queue_sequence.clear();
}
buffer_wait_event.GetWritableEvent().Signal();
buffer_wait_event->GetWritableEvent().Signal();
is_connect = false;
free_buffers_condition.notify_one();
}
@@ -189,11 +192,11 @@ u32 BufferQueue::Query(QueryType type) {
}
Kernel::KWritableEvent& BufferQueue::GetWritableBufferWaitEvent() {
return buffer_wait_event.GetWritableEvent();
return buffer_wait_event->GetWritableEvent();
}
Kernel::KReadableEvent& BufferQueue::GetBufferWaitEvent() {
return buffer_wait_event.GetReadableEvent();
return buffer_wait_event->GetReadableEvent();
}
} // namespace Service::NVFlinger

View File

@@ -24,6 +24,10 @@ class KReadableEvent;
class KWritableEvent;
} // namespace Kernel
namespace Service::KernelHelpers {
class ServiceContext;
} // namespace Service::KernelHelpers
namespace Service::NVFlinger {
constexpr u32 buffer_slots = 0x40;
@@ -54,7 +58,8 @@ public:
NativeWindowFormat = 2,
};
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_);
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
KernelHelpers::ServiceContext& service_context_);
~BufferQueue();
enum class BufferTransformFlags : u32 {
@@ -130,12 +135,14 @@ private:
std::list<u32> free_buffers;
std::array<Buffer, buffer_slots> buffers;
std::list<u32> queue_sequence;
Kernel::KEvent buffer_wait_event;
Kernel::KEvent* buffer_wait_event{};
std::mutex free_buffers_mutex;
std::condition_variable free_buffers_condition;
std::mutex queue_sequence_mutex;
KernelHelpers::ServiceContext& service_context;
};
} // namespace Service::NVFlinger

View File

@@ -61,12 +61,13 @@ void NVFlinger::SplitVSync() {
}
}
NVFlinger::NVFlinger(Core::System& system_) : system(system_) {
displays.emplace_back(0, "Default", system);
displays.emplace_back(1, "External", system);
displays.emplace_back(2, "Edid", system);
displays.emplace_back(3, "Internal", system);
displays.emplace_back(4, "Null", system);
NVFlinger::NVFlinger(Core::System& system_)
: system(system_), service_context(system_, "nvflinger") {
displays.emplace_back(0, "Default", service_context, system);
displays.emplace_back(1, "External", service_context, system);
displays.emplace_back(2, "Edid", service_context, system);
displays.emplace_back(3, "Internal", service_context, system);
displays.emplace_back(4, "Null", service_context, system);
guard = std::make_shared<std::mutex>();
// Schedule the screen composition events
@@ -146,7 +147,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
void NVFlinger::CreateLayerAtId(VI::Display& display, u64 layer_id) {
const u32 buffer_queue_id = next_buffer_queue_id++;
buffer_queues.emplace_back(
std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id));
std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id, service_context));
display.CreateLayer(layer_id, *buffer_queues.back());
}

View File

@@ -15,6 +15,7 @@
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/kernel_helpers.h"
namespace Common {
class Event;
@@ -135,6 +136,8 @@ private:
std::unique_ptr<std::thread> vsync_thread;
std::unique_ptr<Common::Event> wait_event;
std::atomic<bool> is_running{};
KernelHelpers::ServiceContext service_context;
};
} // namespace Service::NVFlinger

View File

@@ -12,18 +12,21 @@
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_writable_event.h"
#include "core/hle/service/kernel_helpers.h"
#include "core/hle/service/vi/display/vi_display.h"
#include "core/hle/service/vi/layer/vi_layer.h"
namespace Service::VI {
Display::Display(u64 id, std::string name_, Core::System& system)
: display_id{id}, name{std::move(name_)}, vsync_event{system.Kernel()} {
Kernel::KAutoObject::Create(std::addressof(vsync_event));
vsync_event.Initialize(fmt::format("Display VSync Event {}", id));
Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
Core::System& system_)
: display_id{id}, name{std::move(name_)}, service_context{service_context_} {
vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id));
}
Display::~Display() = default;
Display::~Display() {
service_context.CloseEvent(vsync_event);
}
Layer& Display::GetLayer(std::size_t index) {
return *layers.at(index);
@@ -34,11 +37,11 @@ const Layer& Display::GetLayer(std::size_t index) const {
}
Kernel::KReadableEvent& Display::GetVSyncEvent() {
return vsync_event.GetReadableEvent();
return vsync_event->GetReadableEvent();
}
void Display::SignalVSyncEvent() {
vsync_event.GetWritableEvent().Signal();
vsync_event->GetWritableEvent().Signal();
}
void Display::CreateLayer(u64 layer_id, NVFlinger::BufferQueue& buffer_queue) {

View File

@@ -18,6 +18,9 @@ class KEvent;
namespace Service::NVFlinger {
class BufferQueue;
}
namespace Service::KernelHelpers {
class ServiceContext;
} // namespace Service::KernelHelpers
namespace Service::VI {
@@ -31,10 +34,13 @@ class Display {
public:
/// Constructs a display with a given unique ID and name.
///
/// @param id The unique ID for this display.
/// @param id The unique ID for this display.
/// @param service_context_ The ServiceContext for the owning service.
/// @param name_ The name for this display.
/// @param system_ The global system instance.
///
Display(u64 id, std::string name_, Core::System& system);
Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
Core::System& system_);
~Display();
/// Gets the unique ID assigned to this display.
@@ -98,9 +104,10 @@ public:
private:
u64 display_id;
std::string name;
KernelHelpers::ServiceContext& service_context;
std::vector<std::shared_ptr<Layer>> layers;
Kernel::KEvent vsync_event;
Kernel::KEvent* vsync_event{};
};
} // namespace Service::VI

View File

@@ -1158,7 +1158,7 @@ private:
const auto layer_id = nv_flinger.CreateLayer(display_id);
if (!layer_id) {
LOG_ERROR(Service_VI, "Layer not found! layer_id={}", *layer_id);
LOG_ERROR(Service_VI, "Layer not found! display_id={}", display_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NOT_FOUND);
return;

View File

@@ -10,9 +10,10 @@
#include "common/common_funcs.h"
#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS // gethostname
#include <winsock2.h>
#include <ws2tcpip.h>
#elif YUZU_UNIX
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
@@ -27,7 +28,9 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "core/network/network.h"
#include "core/network/network_interface.h"
#include "core/network/sockets.h"
namespace Network {
@@ -47,11 +50,6 @@ void Finalize() {
WSACleanup();
}
constexpr IPv4Address TranslateIPv4(in_addr addr) {
auto& bytes = addr.S_un.S_un_b;
return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
}
sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
sockaddr_in result;
@@ -138,12 +136,6 @@ void Initialize() {}
void Finalize() {}
constexpr IPv4Address TranslateIPv4(in_addr addr) {
const u32 bytes = addr.s_addr;
return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
}
sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
sockaddr_in result;
@@ -182,7 +174,7 @@ linger MakeLinger(bool enable, u32 linger_value) {
}
bool EnableNonBlock(int fd, bool enable) {
int flags = fcntl(fd, F_GETFD);
int flags = fcntl(fd, F_GETFL);
if (flags == -1) {
return false;
}
@@ -191,7 +183,7 @@ bool EnableNonBlock(int fd, bool enable) {
} else {
flags &= ~O_NONBLOCK;
}
return fcntl(fd, F_SETFD, flags) == 0;
return fcntl(fd, F_SETFL, flags) == 0;
}
Errno TranslateNativeError(int e) {
@@ -227,8 +219,12 @@ Errno GetAndLogLastError() {
#else
int e = errno;
#endif
const Errno err = TranslateNativeError(e);
if (err == Errno::AGAIN) {
return err;
}
LOG_ERROR(Network, "Socket operation error: {}", NativeErrorToString(e));
return TranslateNativeError(e);
return err;
}
int TranslateDomain(Domain domain) {
@@ -353,27 +349,29 @@ NetworkInstance::~NetworkInstance() {
Finalize();
}
std::pair<IPv4Address, Errno> GetHostIPv4Address() {
std::array<char, 256> name{};
if (gethostname(name.data(), static_cast<int>(name.size()) - 1) == SOCKET_ERROR) {
return {IPv4Address{}, GetAndLogLastError()};
std::optional<IPv4Address> GetHostIPv4Address() {
const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
if (network_interfaces.size() == 0) {
LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
return {};
}
hostent* const ent = gethostbyname(name.data());
if (!ent) {
return {IPv4Address{}, GetAndLogLastError()};
}
if (ent->h_addr_list == nullptr) {
UNIMPLEMENTED_MSG("No addr provided in hostent->h_addr_list");
return {IPv4Address{}, Errno::SUCCESS};
}
if (ent->h_length != sizeof(in_addr)) {
UNIMPLEMENTED_MSG("Unexpected size={} in hostent->h_length", ent->h_length);
}
const auto res =
std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
return iface.name == selected_network_interface;
});
in_addr addr;
std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr));
return {TranslateIPv4(addr), Errno::SUCCESS};
if (res != network_interfaces.end()) {
char ip_addr[16] = {};
ASSERT(inet_ntop(AF_INET, &res->ip_address, ip_addr, sizeof(ip_addr)) != nullptr);
LOG_INFO(Network, "IP address: {}", ip_addr);
return TranslateIPv4(res->ip_address);
} else {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
return {};
}
}
std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {

View File

@@ -5,11 +5,18 @@
#pragma once
#include <array>
#include <optional>
#include <utility>
#include "common/common_funcs.h"
#include "common/common_types.h"
#ifdef _WIN32
#include <winsock2.h>
#elif YUZU_UNIX
#include <netinet/in.h>
#endif
namespace Network {
class Socket;
@@ -92,8 +99,21 @@ public:
~NetworkInstance();
};
#ifdef _WIN32
constexpr IPv4Address TranslateIPv4(in_addr addr) {
auto& bytes = addr.S_un.S_un_b;
return IPv4Address{bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4};
}
#elif YUZU_UNIX
constexpr IPv4Address TranslateIPv4(in_addr addr) {
const u32 bytes = addr.s_addr;
return IPv4Address{static_cast<u8>(bytes), static_cast<u8>(bytes >> 8),
static_cast<u8>(bytes >> 16), static_cast<u8>(bytes >> 24)};
}
#endif
/// @brief Returns host's IPv4 address
/// @return Pair of an array of human ordered IPv4 address (e.g. 192.168.0.1) and an error code
std::pair<IPv4Address, Errno> GetHostIPv4Address();
/// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array
std::optional<IPv4Address> GetHostIPv4Address();
} // namespace Network

View File

@@ -0,0 +1,203 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <fstream>
#include <sstream>
#include <vector>
#include "common/bit_cast.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "common/string_util.h"
#include "core/network/network_interface.h"
#ifdef _WIN32
#include <iphlpapi.h>
#else
#include <cerrno>
#include <ifaddrs.h>
#include <net/if.h>
#endif
namespace Network {
#ifdef _WIN32
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<IP_ADAPTER_ADDRESSES> adapter_addresses;
DWORD ret = ERROR_BUFFER_OVERFLOW;
DWORD buf_size = 0;
// retry up to 5 times
for (int i = 0; i < 5 && ret == ERROR_BUFFER_OVERFLOW; i++) {
ret = GetAdaptersAddresses(
AF_INET, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_GATEWAYS,
nullptr, adapter_addresses.data(), &buf_size);
if (ret == ERROR_BUFFER_OVERFLOW) {
adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
} else {
break;
}
}
if (ret == NO_ERROR) {
std::vector<NetworkInterface> result;
for (auto current_address = adapter_addresses.data(); current_address != nullptr;
current_address = current_address->Next) {
if (current_address->FirstUnicastAddress == nullptr ||
current_address->FirstUnicastAddress->Address.lpSockaddr == nullptr) {
continue;
}
if (current_address->OperStatus != IfOperStatusUp) {
continue;
}
const auto ip_addr = Common::BitCast<struct sockaddr_in>(
*current_address->FirstUnicastAddress->Address.lpSockaddr)
.sin_addr;
ULONG mask = 0;
if (ConvertLengthToIpv4Mask(current_address->FirstUnicastAddress->OnLinkPrefixLength,
&mask) != NO_ERROR) {
LOG_ERROR(Network, "Failed to convert IPv4 prefix length to subnet mask");
continue;
}
struct in_addr gateway = {.S_un{.S_addr{0}}};
if (current_address->FirstGatewayAddress != nullptr &&
current_address->FirstGatewayAddress->Address.lpSockaddr != nullptr) {
gateway = Common::BitCast<struct sockaddr_in>(
*current_address->FirstGatewayAddress->Address.lpSockaddr)
.sin_addr;
}
result.push_back(NetworkInterface{
.name{Common::UTF16ToUTF8(std::wstring{current_address->FriendlyName})},
.ip_address{ip_addr},
.subnet_mask = in_addr{.S_un{.S_addr{mask}}},
.gateway = gateway});
}
return result;
} else {
LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
return {};
}
}
#else
std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
std::vector<NetworkInterface> result;
struct ifaddrs* ifaddr = nullptr;
if (getifaddrs(&ifaddr) != 0) {
LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
std::strerror(errno));
return result;
}
for (auto ifa = ifaddr; ifa != nullptr; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == nullptr || ifa->ifa_netmask == nullptr) {
continue;
}
if (ifa->ifa_addr->sa_family != AF_INET) {
continue;
}
if ((ifa->ifa_flags & IFF_UP) == 0 || (ifa->ifa_flags & IFF_LOOPBACK) != 0) {
continue;
}
std::uint32_t gateway{0};
std::ifstream file{"/proc/net/route"};
if (file.is_open()) {
// ignore header
file.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
bool gateway_found = false;
for (std::string line; std::getline(file, line);) {
std::istringstream iss{line};
std::string iface_name{};
iss >> iface_name;
if (iface_name != ifa->ifa_name) {
continue;
}
iss >> std::hex;
std::uint32_t dest{0};
iss >> dest;
if (dest != 0) {
// not the default route
continue;
}
iss >> gateway;
std::uint16_t flags{0};
iss >> flags;
// flag RTF_GATEWAY (defined in <linux/route.h>)
if ((flags & 0x2) == 0) {
continue;
}
gateway_found = true;
break;
}
if (!gateway_found) {
gateway = 0;
}
} else {
LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
}
result.push_back(NetworkInterface{
.name{ifa->ifa_name},
.ip_address{Common::BitCast<struct sockaddr_in>(*ifa->ifa_addr).sin_addr},
.subnet_mask{Common::BitCast<struct sockaddr_in>(*ifa->ifa_netmask).sin_addr},
.gateway{in_addr{.s_addr = gateway}}});
}
freeifaddrs(ifaddr);
return result;
}
#endif
std::optional<NetworkInterface> GetSelectedNetworkInterface() {
const std::string& selected_network_interface = Settings::values.network_interface.GetValue();
const auto network_interfaces = Network::GetAvailableNetworkInterfaces();
if (network_interfaces.size() == 0) {
LOG_ERROR(Network, "GetAvailableNetworkInterfaces returned no interfaces");
return {};
}
const auto res =
std::ranges::find_if(network_interfaces, [&selected_network_interface](const auto& iface) {
return iface.name == selected_network_interface;
});
if (res != network_interfaces.end()) {
return *res;
} else {
LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
return {};
}
}
} // namespace Network

View File

@@ -0,0 +1,29 @@
// Copyright 2021 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <optional>
#include <string>
#include <vector>
#ifdef _WIN32
#include <winsock2.h>
#else
#include <netinet/in.h>
#endif
namespace Network {
struct NetworkInterface {
std::string name;
struct in_addr ip_address;
struct in_addr subnet_mask;
struct in_addr gateway;
};
std::vector<NetworkInterface> GetAvailableNetworkInterfaces();
std::optional<NetworkInterface> GetSelectedNetworkInterface();
} // namespace Network

View File

@@ -298,14 +298,10 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
if (IR::IsGeneric(attr)) {
const u32 index{IR::GenericAttributeIndex(attr)};
const std::optional<AttrInfo> type{AttrTypes(ctx, index)};
if (!type) {
// Attribute is disabled
if (!type || !ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
// Attribute is disabled or varying component is not written
return ctx.Const(element == 3 ? 1.0f : 0.0f);
}
if (!ctx.runtime_info.previous_stage_stores.Generic(index, element)) {
// Varying component is not written
return ctx.Const(type && element == 3 ? 1.0f : 0.0f);
}
const Id generic_id{ctx.input_generics.at(index)};
const Id pointer{AttrPointer(ctx, type->pointer, vertex, generic_id, ctx.Const(element))};
const Id value{ctx.OpLoad(type->id, pointer)};

View File

@@ -4,11 +4,13 @@
#include <catch2/catch.hpp>
#include <math.h>
#include "common/logging/backend.h"
#include "common/param_package.h"
namespace Common {
TEST_CASE("ParamPackage", "[common]") {
Common::Log::DisableLoggingInTests();
ParamPackage original{
{"abc", "xyz"},
{"def", "42"},

View File

@@ -6,7 +6,7 @@
#include <array>
#include <bitset>
#include <xbyak.h>
#include <xbyak/xbyak.h>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/x64/xbyak_abi.h"

View File

@@ -463,6 +463,7 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
++page_index;
page_offset = 0;
remaining_size -= num_bytes;
old_page_addr = page_addr;
}
split();
return result;

View File

@@ -356,7 +356,7 @@ void VKBlitScreen::CreateDescriptorPool() {
void VKBlitScreen::CreateRenderPass() {
const VkAttachmentDescription color_attachment{
.flags = 0,
.format = swapchain.GetImageFormat(),
.format = swapchain.GetImageViewFormat(),
.samples = VK_SAMPLE_COUNT_1_BIT,
.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
.storeOp = VK_ATTACHMENT_STORE_OP_STORE,

View File

@@ -281,7 +281,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw
.supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U,
.unified_descriptor_binding = true,
.support_descriptor_aliasing = true,
.support_int8 = true,
.support_int8 = device.IsInt8Supported(),
.support_int16 = device.IsShaderInt16Supported(),
.support_int64 = device.IsShaderInt64Supported(),
.support_vertex_instance_id = false,

View File

@@ -20,16 +20,15 @@ namespace Vulkan {
namespace {
VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats, bool srgb) {
VkSurfaceFormatKHR ChooseSwapSurfaceFormat(vk::Span<VkSurfaceFormatKHR> formats) {
if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
VkSurfaceFormatKHR format;
format.format = VK_FORMAT_B8G8R8A8_UNORM;
format.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
return format;
}
const auto& found = std::find_if(formats.begin(), formats.end(), [srgb](const auto& format) {
const auto request_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
return format.format == request_format &&
const auto& found = std::find_if(formats.begin(), formats.end(), [](const auto& format) {
return format.format == VK_FORMAT_B8G8R8A8_UNORM &&
format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
});
return found != formats.end() ? *found : formats[0];
@@ -145,7 +144,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
const auto formats{physical_device.GetSurfaceFormatsKHR(surface)};
const auto present_modes{physical_device.GetSurfacePresentModesKHR(surface)};
const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats, srgb)};
const VkSurfaceFormatKHR surface_format{ChooseSwapSurfaceFormat(formats)};
const VkPresentModeKHR present_mode{ChooseSwapPresentMode(present_modes)};
u32 requested_image_count{capabilities.minImageCount + 1};
@@ -180,6 +179,17 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
swapchain_ci.queueFamilyIndexCount = static_cast<u32>(queue_indices.size());
swapchain_ci.pQueueFamilyIndices = queue_indices.data();
}
static constexpr std::array view_formats{VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SRGB};
VkImageFormatListCreateInfo format_list{
.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR,
.pNext = nullptr,
.viewFormatCount = static_cast<u32>(view_formats.size()),
.pViewFormats = view_formats.data(),
};
if (device.IsKhrSwapchainMutableFormatEnabled()) {
format_list.pNext = std::exchange(swapchain_ci.pNext, &format_list);
swapchain_ci.flags |= VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR;
}
// Request the size again to reduce the possibility of a TOCTOU race condition.
const auto updated_capabilities = physical_device.GetSurfaceCapabilitiesKHR(surface);
swapchain_ci.imageExtent = ChooseSwapExtent(updated_capabilities, width, height);
@@ -191,7 +201,7 @@ void VKSwapchain::CreateSwapchain(const VkSurfaceCapabilitiesKHR& capabilities,
images = swapchain.GetImages();
image_count = static_cast<u32>(images.size());
image_format = surface_format.format;
image_view_format = srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
}
void VKSwapchain::CreateSemaphores() {
@@ -207,7 +217,7 @@ void VKSwapchain::CreateImageViews() {
.flags = 0,
.image = {},
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = image_format,
.format = image_view_format,
.components =
{
.r = VK_COMPONENT_SWIZZLE_IDENTITY,

View File

@@ -68,8 +68,8 @@ public:
return *image_views[index];
}
VkFormat GetImageFormat() const {
return image_format;
VkFormat GetImageViewFormat() const {
return image_view_format;
}
private:
@@ -96,7 +96,7 @@ private:
u32 image_index{};
u32 frame_index{};
VkFormat image_format{};
VkFormat image_view_format{};
VkExtent2D extent{};
bool current_srgb{};

View File

@@ -84,34 +84,107 @@ template <bool TO_LINEAR>
void Swizzle(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel, u32 width,
u32 height, u32 depth, u32 block_height, u32 block_depth, u32 stride_alignment) {
switch (bytes_per_pixel) {
case 1:
return SwizzleImpl<TO_LINEAR, 1>(output, input, width, height, depth, block_height,
#define BPP_CASE(x) \
case x: \
return SwizzleImpl<TO_LINEAR, x>(output, input, width, height, depth, block_height, \
block_depth, stride_alignment);
case 2:
return SwizzleImpl<TO_LINEAR, 2>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 3:
return SwizzleImpl<TO_LINEAR, 3>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 4:
return SwizzleImpl<TO_LINEAR, 4>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 6:
return SwizzleImpl<TO_LINEAR, 6>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 8:
return SwizzleImpl<TO_LINEAR, 8>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 12:
return SwizzleImpl<TO_LINEAR, 12>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
case 16:
return SwizzleImpl<TO_LINEAR, 16>(output, input, width, height, depth, block_height,
block_depth, stride_alignment);
BPP_CASE(1)
BPP_CASE(2)
BPP_CASE(3)
BPP_CASE(4)
BPP_CASE(6)
BPP_CASE(8)
BPP_CASE(12)
BPP_CASE(16)
#undef BPP_CASE
default:
UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
}
}
template <u32 BYTES_PER_PIXEL>
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u8* swizzled_data, const u8* unswizzled_data, u32 block_height_bit,
u32 offset_x, u32 offset_y) {
const u32 block_height = 1U << block_height_bit;
const u32 image_width_in_gobs =
(swizzled_width * BYTES_PER_PIXEL + (GOB_SIZE_X - 1)) / GOB_SIZE_X;
for (u32 line = 0; line < subrect_height; ++line) {
const u32 dst_y = line + offset_y;
const u32 gob_address_y =
(dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
const auto& table = SWIZZLE_TABLE[dst_y % GOB_SIZE_Y];
for (u32 x = 0; x < subrect_width; ++x) {
const u32 dst_x = x + offset_x;
const u32 gob_address =
gob_address_y + (dst_x * BYTES_PER_PIXEL / GOB_SIZE_X) * GOB_SIZE * block_height;
const u32 swizzled_offset = gob_address + table[(dst_x * BYTES_PER_PIXEL) % GOB_SIZE_X];
const u32 unswizzled_offset = line * source_pitch + x * BYTES_PER_PIXEL;
const u8* const source_line = unswizzled_data + unswizzled_offset;
u8* const dest_addr = swizzled_data + swizzled_offset;
std::memcpy(dest_addr, source_line, BYTES_PER_PIXEL);
}
}
}
template <u32 BYTES_PER_PIXEL>
void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 block_height,
u32 origin_x, u32 origin_y, u8* output, const u8* input) {
const u32 stride = width * BYTES_PER_PIXEL;
const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height);
const u32 block_height_mask = (1U << block_height) - 1;
const u32 x_shift = GOB_SIZE_SHIFT + block_height;
for (u32 line = 0; line < line_count; ++line) {
const u32 src_y = line + origin_y;
const auto& table = SWIZZLE_TABLE[src_y % GOB_SIZE_Y];
const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT;
const u32 src_offset_y = (block_y >> block_height) * block_size +
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
for (u32 column = 0; column < line_length_in; ++column) {
const u32 src_x = (column + origin_x) * BYTES_PER_PIXEL;
const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift;
const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X];
const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL;
std::memcpy(output + unswizzled_offset, input + swizzled_offset, BYTES_PER_PIXEL);
}
}
}
template <u32 BYTES_PER_PIXEL>
void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
u32 block_height, u32 block_depth, u32 origin_x, u32 origin_y, u8* output,
const u8* input) {
UNIMPLEMENTED_IF(origin_x > 0);
UNIMPLEMENTED_IF(origin_y > 0);
const u32 stride = width * BYTES_PER_PIXEL;
const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
const u32 block_height_mask = (1U << block_height) - 1;
const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth;
for (u32 line = 0; line < line_count; ++line) {
const auto& table = SWIZZLE_TABLE[line % GOB_SIZE_Y];
const u32 block_y = line / GOB_SIZE_Y;
const u32 dst_offset_y =
(block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE;
for (u32 x = 0; x < line_length_in; ++x) {
const u32 dst_offset =
((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X];
const u32 src_offset = x * BYTES_PER_PIXEL + line * pitch;
std::memcpy(output + dst_offset, input + src_offset, BYTES_PER_PIXEL);
}
}
}
} // Anonymous namespace
void UnswizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_per_pixel,
@@ -131,81 +204,67 @@ void SwizzleTexture(std::span<u8> output, std::span<const u8> input, u32 bytes_p
void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32 swizzled_width,
u32 bytes_per_pixel, u8* swizzled_data, const u8* unswizzled_data,
u32 block_height_bit, u32 offset_x, u32 offset_y) {
const u32 block_height = 1U << block_height_bit;
const u32 image_width_in_gobs =
(swizzled_width * bytes_per_pixel + (GOB_SIZE_X - 1)) / GOB_SIZE_X;
for (u32 line = 0; line < subrect_height; ++line) {
const u32 dst_y = line + offset_y;
const u32 gob_address_y =
(dst_y / (GOB_SIZE_Y * block_height)) * GOB_SIZE * block_height * image_width_in_gobs +
((dst_y % (GOB_SIZE_Y * block_height)) / GOB_SIZE_Y) * GOB_SIZE;
const auto& table = SWIZZLE_TABLE[dst_y % GOB_SIZE_Y];
for (u32 x = 0; x < subrect_width; ++x) {
const u32 dst_x = x + offset_x;
const u32 gob_address =
gob_address_y + (dst_x * bytes_per_pixel / GOB_SIZE_X) * GOB_SIZE * block_height;
const u32 swizzled_offset = gob_address + table[(dst_x * bytes_per_pixel) % GOB_SIZE_X];
const u32 unswizzled_offset = line * source_pitch + x * bytes_per_pixel;
const u8* const source_line = unswizzled_data + unswizzled_offset;
u8* const dest_addr = swizzled_data + swizzled_offset;
std::memcpy(dest_addr, source_line, bytes_per_pixel);
}
switch (bytes_per_pixel) {
#define BPP_CASE(x) \
case x: \
return SwizzleSubrect<x>(subrect_width, subrect_height, source_pitch, swizzled_width, \
swizzled_data, unswizzled_data, block_height_bit, offset_x, \
offset_y);
BPP_CASE(1)
BPP_CASE(2)
BPP_CASE(3)
BPP_CASE(4)
BPP_CASE(6)
BPP_CASE(8)
BPP_CASE(12)
BPP_CASE(16)
#undef BPP_CASE
default:
UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
}
}
void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 bytes_per_pixel,
u32 block_height, u32 origin_x, u32 origin_y, u8* output, const u8* input) {
const u32 stride = width * bytes_per_pixel;
const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height);
const u32 block_height_mask = (1U << block_height) - 1;
const u32 x_shift = GOB_SIZE_SHIFT + block_height;
for (u32 line = 0; line < line_count; ++line) {
const u32 src_y = line + origin_y;
const auto& table = SWIZZLE_TABLE[src_y % GOB_SIZE_Y];
const u32 block_y = src_y >> GOB_SIZE_Y_SHIFT;
const u32 src_offset_y = (block_y >> block_height) * block_size +
((block_y & block_height_mask) << GOB_SIZE_SHIFT);
for (u32 column = 0; column < line_length_in; ++column) {
const u32 src_x = (column + origin_x) * bytes_per_pixel;
const u32 src_offset_x = (src_x >> GOB_SIZE_X_SHIFT) << x_shift;
const u32 swizzled_offset = src_offset_y + src_offset_x + table[src_x % GOB_SIZE_X];
const u32 unswizzled_offset = line * pitch + column * bytes_per_pixel;
std::memcpy(output + unswizzled_offset, input + swizzled_offset, bytes_per_pixel);
}
switch (bytes_per_pixel) {
#define BPP_CASE(x) \
case x: \
return UnswizzleSubrect<x>(line_length_in, line_count, pitch, width, block_height, \
origin_x, origin_y, output, input);
BPP_CASE(1)
BPP_CASE(2)
BPP_CASE(3)
BPP_CASE(4)
BPP_CASE(6)
BPP_CASE(8)
BPP_CASE(12)
BPP_CASE(16)
#undef BPP_CASE
default:
UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
}
}
void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 width, u32 height,
u32 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
u32 origin_y, u8* output, const u8* input) {
UNIMPLEMENTED_IF(origin_x > 0);
UNIMPLEMENTED_IF(origin_y > 0);
const u32 stride = width * bytes_per_pixel;
const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) / GOB_SIZE_X;
const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block_height + block_depth);
const u32 block_height_mask = (1U << block_height) - 1;
const u32 x_shift = static_cast<u32>(GOB_SIZE_SHIFT) + block_height + block_depth;
for (u32 line = 0; line < line_count; ++line) {
const auto& table = SWIZZLE_TABLE[line % GOB_SIZE_Y];
const u32 block_y = line / GOB_SIZE_Y;
const u32 dst_offset_y =
(block_y >> block_height) * block_size + (block_y & block_height_mask) * GOB_SIZE;
for (u32 x = 0; x < line_length_in; ++x) {
const u32 dst_offset =
((x / GOB_SIZE_X) << x_shift) + dst_offset_y + table[x % GOB_SIZE_X];
const u32 src_offset = x * bytes_per_pixel + line * pitch;
std::memcpy(output + dst_offset, input + src_offset, bytes_per_pixel);
}
switch (bytes_per_pixel) {
#define BPP_CASE(x) \
case x: \
return SwizzleSliceToVoxel<x>(line_length_in, line_count, pitch, width, height, \
block_height, block_depth, origin_x, origin_y, output, \
input);
BPP_CASE(1)
BPP_CASE(2)
BPP_CASE(3)
BPP_CASE(4)
BPP_CASE(6)
BPP_CASE(8)
BPP_CASE(12)
BPP_CASE(16)
#undef BPP_CASE
default:
UNREACHABLE_MSG("Invalid bytes_per_pixel={}", bytes_per_pixel);
}
}
@@ -228,7 +287,7 @@ void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32
u8* dest_addr = swizzle_data + swizzled_offset;
count++;
std::memcpy(dest_addr, source_line, 1);
*dest_addr = *source_line;
}
}
}

View File

@@ -159,7 +159,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
return {raw, raw};
} else {
const Tegra::Texture::TextureHandle handle{raw};
return {handle.tic_id, via_header_index ? handle.tic_id : handle.tsc_id};
return {handle.tic_id, handle.tsc_id};
}
}

View File

@@ -368,18 +368,21 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
};
SetNext(next, demote);
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8;
if (is_float16_supported) {
float16_int8 = {
if (is_int8_supported || is_float16_supported) {
VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR,
.pNext = nullptr,
.shaderFloat16 = true,
.shaderInt8 = false,
.shaderFloat16 = is_float16_supported,
.shaderInt8 = is_int8_supported,
};
SetNext(next, float16_int8);
} else {
}
if (!is_float16_supported) {
LOG_INFO(Render_Vulkan, "Device doesn't support float16 natively");
}
if (!is_int8_supported) {
LOG_INFO(Render_Vulkan, "Device doesn't support int8 natively");
}
if (!nv_viewport_swizzle) {
LOG_INFO(Render_Vulkan, "Device doesn't support viewport swizzles");
@@ -836,6 +839,8 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
bool has_khr_shader_float16_int8{};
bool has_khr_workgroup_memory_explicit_layout{};
bool has_khr_pipeline_executable_properties{};
bool has_khr_image_format_list{};
bool has_khr_swapchain_mutable_format{};
bool has_ext_subgroup_size_control{};
bool has_ext_transform_feedback{};
bool has_ext_custom_border_color{};
@@ -885,6 +890,9 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
test(has_ext_shader_atomic_int64, VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME, false);
test(has_khr_workgroup_memory_explicit_layout,
VK_KHR_WORKGROUP_MEMORY_EXPLICIT_LAYOUT_EXTENSION_NAME, false);
test(has_khr_image_format_list, VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false);
test(has_khr_swapchain_mutable_format, VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME,
false);
test(has_ext_line_rasterization, VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME, false);
if (Settings::values.enable_nsight_aftermath) {
test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
@@ -909,6 +917,7 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
physical.GetFeatures2KHR(features);
is_float16_supported = float16_int8_features.shaderFloat16;
is_int8_supported = float16_int8_features.shaderInt8;
extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
}
if (has_ext_subgroup_size_control) {
@@ -1062,6 +1071,11 @@ std::vector<const char*> Device::LoadExtensions(bool requires_surface) {
khr_pipeline_executable_properties = true;
}
}
if (has_khr_image_format_list && has_khr_swapchain_mutable_format) {
extensions.push_back(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME);
extensions.push_back(VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME);
khr_swapchain_mutable_format = true;
}
if (khr_push_descriptor) {
VkPhysicalDevicePushDescriptorPropertiesKHR push_descriptor;
push_descriptor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR;

View File

@@ -139,11 +139,16 @@ public:
return is_optimal_astc_supported;
}
/// Returns true if the device supports float16 natively
/// Returns true if the device supports float16 natively.
bool IsFloat16Supported() const {
return is_float16_supported;
}
/// Returns true if the device supports int8 natively.
bool IsInt8Supported() const {
return is_int8_supported;
}
/// Returns true if the device warp size can potentially be bigger than guest's warp size.
bool IsWarpSizePotentiallyBiggerThanGuest() const {
return is_warp_potentially_bigger;
@@ -219,6 +224,11 @@ public:
return khr_pipeline_executable_properties;
}
/// Returns true if VK_KHR_swapchain_mutable_format is enabled.
bool IsKhrSwapchainMutableFormatEnabled() const {
return khr_swapchain_mutable_format;
}
/// Returns true if the device supports VK_KHR_workgroup_memory_explicit_layout.
bool IsKhrWorkgroupMemoryExplicitLayoutSupported() const {
return khr_workgroup_memory_explicit_layout;
@@ -367,7 +377,8 @@ private:
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
u32 max_push_descriptors{}; ///< Maximum number of push descriptors
bool is_optimal_astc_supported{}; ///< Support for native ASTC.
bool is_float16_supported{}; ///< Support for float16 arithmetics.
bool is_float16_supported{}; ///< Support for float16 arithmetic.
bool is_int8_supported{}; ///< Support for int8 arithmetic.
bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest.
bool is_formatless_image_load_supported{}; ///< Support for shader image read without format.
bool is_depth_bounds_supported{}; ///< Support for depth bounds.
@@ -384,6 +395,7 @@ private:
bool khr_workgroup_memory_explicit_layout{}; ///< Support for explicit workgroup layouts.
bool khr_push_descriptor{}; ///< Support for VK_KHR_push_descritor.
bool khr_pipeline_executable_properties{}; ///< Support for executable properties.
bool khr_swapchain_mutable_format{}; ///< Support for VK_KHR_swapchain_mutable_format.
bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8.
bool ext_sampler_filter_minmax{}; ///< Support for VK_EXT_sampler_filter_minmax.
bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted.

View File

@@ -102,9 +102,9 @@ add_executable(yuzu
configuration/configure_profile_manager.cpp
configuration/configure_profile_manager.h
configuration/configure_profile_manager.ui
configuration/configure_service.cpp
configuration/configure_service.h
configuration/configure_service.ui
configuration/configure_network.cpp
configuration/configure_network.h
configuration/configure_network.ui
configuration/configure_system.cpp
configuration/configure_system.h
configuration/configure_system.ui
@@ -182,7 +182,14 @@ if (ENABLE_QT_TRANSLATION)
# Update source TS file if enabled
if (GENERATE_QT_TRANSLATION)
get_target_property(SRCS yuzu SOURCES)
qt5_create_translation(QM_FILES ${SRCS} ${UIS} ${YUZU_QT_LANGUAGES}/en.ts)
qt5_create_translation(QM_FILES
${SRCS}
${UIS}
${YUZU_QT_LANGUAGES}/en.ts
OPTIONS
-source-language en_US
-target-language en_US
)
add_custom_target(translation ALL DEPENDS ${YUZU_QT_LANGUAGES}/en.ts)
endif()

View File

@@ -438,7 +438,7 @@ void QtSoftwareKeyboardDialog::ShowInlineKeyboard(
initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags;
initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button;
initialize_parameters.enable_return_button = appear_parameters.enable_return_button;
initialize_parameters.disable_cancel_button = initialize_parameters.disable_cancel_button;
initialize_parameters.disable_cancel_button = appear_parameters.disable_cancel_button;
SetKeyboardType();
SetControllerImage();

View File

@@ -692,6 +692,7 @@ void Config::ReadServiceValues() {
qt_config->beginGroup(QStringLiteral("Services"));
ReadBasicSetting(Settings::values.bcat_backend);
ReadBasicSetting(Settings::values.bcat_boxcat_local);
ReadBasicSetting(Settings::values.network_interface);
qt_config->endGroup();
}
@@ -1144,7 +1145,7 @@ void Config::SaveValues() {
SaveDataStorageValues();
SaveDebuggingValues();
SaveDisabledAddOnValues();
SaveServiceValues();
SaveNetworkValues();
SaveUIValues();
SaveWebServiceValues();
SaveMiscellaneousValues();
@@ -1238,11 +1239,12 @@ void Config::SaveDebuggingValues() {
qt_config->endGroup();
}
void Config::SaveServiceValues() {
void Config::SaveNetworkValues() {
qt_config->beginGroup(QStringLiteral("Services"));
WriteBasicSetting(Settings::values.bcat_backend);
WriteBasicSetting(Settings::values.bcat_boxcat_local);
WriteBasicSetting(Settings::values.network_interface);
qt_config->endGroup();
}

View File

@@ -88,7 +88,7 @@ private:
void SaveCoreValues();
void SaveDataStorageValues();
void SaveDebuggingValues();
void SaveServiceValues();
void SaveNetworkValues();
void SaveDisabledAddOnValues();
void SaveMiscellaneousValues();
void SavePathValues();

View File

@@ -147,12 +147,12 @@
<string>Web</string>
</attribute>
</widget>
<widget class="ConfigureService" name="serviceTab">
<widget class="ConfigureNetwork" name="networkTab">
<property name="accessibleName">
<string>Services</string>
<string>Network</string>
</property>
<attribute name="title">
<string>Services</string>
<string>Network</string>
</attribute>
</widget>
</widget>
@@ -242,9 +242,9 @@
<container>1</container>
</customwidget>
<customwidget>
<class>ConfigureService</class>
<class>ConfigureNetwork</class>
<extends>QWidget</extends>
<header>configuration/configure_service.h</header>
<header>configuration/configure_network.h</header>
<container>1</container>
</customwidget>
<customwidget>

View File

@@ -67,7 +67,7 @@ void ConfigureDialog::ApplyConfiguration() {
ui->audioTab->ApplyConfiguration();
ui->debugTab->ApplyConfiguration();
ui->webTab->ApplyConfiguration();
ui->serviceTab->ApplyConfiguration();
ui->networkTab->ApplyConfiguration();
Core::System::GetInstance().ApplySettings();
Settings::LogSettings();
}
@@ -103,7 +103,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
{{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
{tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
{tr("System"), {ui->systemTab, ui->profileManagerTab, ui->networkTab, ui->filesystemTab}},
{tr("CPU"), {ui->cpuTab}},
{tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
{tr("Audio"), {ui->audioTab}},

View File

@@ -647,18 +647,18 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
// Face buttons
p.setPen(colors.outline);
button_color = colors.button;
DrawCircleButton(p, face_center + QPoint(face_distance, 0), button_values[A], face_radius);
DrawCircleButton(p, face_center + QPoint(0, face_distance), button_values[B], face_radius);
DrawCircleButton(p, face_center + QPoint(0, -face_distance), button_values[X], face_radius);
DrawCircleButton(p, face_center + QPoint(-face_distance, 0), button_values[Y], face_radius);
DrawCircleButton(p, face_center + QPointF(face_distance, 0), button_values[A], face_radius);
DrawCircleButton(p, face_center + QPointF(0, face_distance), button_values[B], face_radius);
DrawCircleButton(p, face_center + QPointF(0, -face_distance), button_values[X], face_radius);
DrawCircleButton(p, face_center + QPointF(-face_distance, 0), button_values[Y], face_radius);
// Face buttons text
p.setPen(colors.transparent);
p.setBrush(colors.font);
DrawSymbol(p, face_center + QPoint(face_distance, 0), Symbol::A, text_size);
DrawSymbol(p, face_center + QPoint(0, face_distance), Symbol::B, text_size);
DrawSymbol(p, face_center + QPoint(0, -face_distance), Symbol::X, text_size);
DrawSymbol(p, face_center + QPoint(-face_distance, 1), Symbol::Y, text_size);
DrawSymbol(p, face_center + QPointF(face_distance, 0), Symbol::A, text_size);
DrawSymbol(p, face_center + QPointF(0, face_distance), Symbol::B, text_size);
DrawSymbol(p, face_center + QPointF(0, -face_distance), Symbol::X, text_size);
DrawSymbol(p, face_center + QPointF(-face_distance, 1), Symbol::Y, text_size);
// D-pad constants
const QPointF dpad_center = center + QPoint(-171, 8);
@@ -669,18 +669,20 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
// D-pad buttons
p.setPen(colors.outline);
button_color = colors.button;
DrawCircleButton(p, dpad_center + QPoint(dpad_distance, 0), button_values[DRight], dpad_radius);
DrawCircleButton(p, dpad_center + QPoint(0, dpad_distance), button_values[DDown], dpad_radius);
DrawCircleButton(p, dpad_center + QPoint(0, -dpad_distance), button_values[DUp], dpad_radius);
DrawCircleButton(p, dpad_center + QPoint(-dpad_distance, 0), button_values[DLeft], dpad_radius);
DrawCircleButton(p, dpad_center + QPointF(dpad_distance, 0), button_values[DRight],
dpad_radius);
DrawCircleButton(p, dpad_center + QPointF(0, dpad_distance), button_values[DDown], dpad_radius);
DrawCircleButton(p, dpad_center + QPointF(0, -dpad_distance), button_values[DUp], dpad_radius);
DrawCircleButton(p, dpad_center + QPointF(-dpad_distance, 0), button_values[DLeft],
dpad_radius);
// D-pad arrows
p.setPen(colors.font2);
p.setBrush(colors.font2);
DrawArrow(p, dpad_center + QPoint(dpad_distance, 0), Direction::Right, dpad_arrow_size);
DrawArrow(p, dpad_center + QPoint(0, dpad_distance), Direction::Down, dpad_arrow_size);
DrawArrow(p, dpad_center + QPoint(0, -dpad_distance), Direction::Up, dpad_arrow_size);
DrawArrow(p, dpad_center + QPoint(-dpad_distance, 0), Direction::Left, dpad_arrow_size);
DrawArrow(p, dpad_center + QPointF(dpad_distance, 0), Direction::Right, dpad_arrow_size);
DrawArrow(p, dpad_center + QPointF(0, dpad_distance), Direction::Down, dpad_arrow_size);
DrawArrow(p, dpad_center + QPointF(0, -dpad_distance), Direction::Up, dpad_arrow_size);
DrawArrow(p, dpad_center + QPointF(-dpad_distance, 0), Direction::Left, dpad_arrow_size);
// ZL and ZR buttons
p.setPen(colors.outline);

View File

@@ -5,9 +5,11 @@
#include <QGraphicsItem>
#include <QtConcurrent/QtConcurrent>
#include "common/settings.h"
#include "core/core.h"
#include "core/hle/service/bcat/backend/boxcat.h"
#include "ui_configure_service.h"
#include "yuzu/configuration/configure_service.h"
#include "core/network/network_interface.h"
#include "ui_configure_network.h"
#include "yuzu/configuration/configure_network.h"
#ifdef YUZU_ENABLE_BOXCAT
namespace {
@@ -35,8 +37,8 @@ QString FormatEventStatusString(const Service::BCAT::EventStatus& status) {
} // Anonymous namespace
#endif
ConfigureService::ConfigureService(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) {
ConfigureNetwork::ConfigureNetwork(QWidget* parent)
: QWidget(parent), ui(std::make_unique<Ui::ConfigureNetwork>()) {
ui->setupUi(this);
ui->bcat_source->addItem(QStringLiteral("None"));
@@ -47,29 +49,42 @@ ConfigureService::ConfigureService(QWidget* parent)
ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat"));
#endif
ui->network_interface->addItem(tr("None"));
for (const auto& iface : Network::GetAvailableNetworkInterfaces()) {
ui->network_interface->addItem(QString::fromStdString(iface.name));
}
connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ConfigureService::OnBCATImplChanged);
&ConfigureNetwork::OnBCATImplChanged);
this->SetConfiguration();
}
ConfigureService::~ConfigureService() = default;
ConfigureNetwork::~ConfigureNetwork() = default;
void ConfigureService::ApplyConfiguration() {
void ConfigureNetwork::ApplyConfiguration() {
Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString();
Settings::values.network_interface = ui->network_interface->currentText().toStdString();
}
void ConfigureService::RetranslateUi() {
void ConfigureNetwork::RetranslateUi() {
ui->retranslateUi(this);
}
void ConfigureService::SetConfiguration() {
void ConfigureNetwork::SetConfiguration() {
const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
const int index =
ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend.GetValue()));
ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index);
const std::string& network_interface = Settings::values.network_interface.GetValue();
ui->network_interface->setCurrentText(QString::fromStdString(network_interface));
ui->network_interface->setEnabled(runtime_lock);
}
std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
std::pair<QString, QString> ConfigureNetwork::BCATDownloadEvents() {
#ifdef YUZU_ENABLE_BOXCAT
std::optional<std::string> global;
std::map<std::string, Service::BCAT::EventStatus> map;
@@ -114,7 +129,7 @@ std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
#endif
}
void ConfigureService::OnBCATImplChanged() {
void ConfigureNetwork::OnBCATImplChanged() {
#ifdef YUZU_ENABLE_BOXCAT
const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
ui->bcat_empty_header->setHidden(!boxcat);
@@ -133,7 +148,7 @@ void ConfigureService::OnBCATImplChanged() {
#endif
}
void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) {
void ConfigureNetwork::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) {
#ifdef YUZU_ENABLE_BOXCAT
const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
if (boxcat) {

View File

@@ -9,15 +9,15 @@
#include <QWidget>
namespace Ui {
class ConfigureService;
class ConfigureNetwork;
}
class ConfigureService : public QWidget {
class ConfigureNetwork : public QWidget {
Q_OBJECT
public:
explicit ConfigureService(QWidget* parent = nullptr);
~ConfigureService() override;
explicit ConfigureNetwork(QWidget* parent = nullptr);
~ConfigureNetwork() override;
void ApplyConfiguration();
void RetranslateUi();
@@ -29,6 +29,6 @@ private:
void OnBCATImplChanged();
void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string);
std::unique_ptr<Ui::ConfigureService> ui;
std::unique_ptr<Ui::ConfigureNetwork> ui;
QFutureWatcher<std::pair<QString, QString>> watcher{this};
};

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureService</class>
<widget class="QWidget" name="ConfigureService">
<class>ConfigureNetwork</class>
<widget class="QWidget" name="ConfigureNetwork">
<property name="geometry">
<rect>
<x>0</x>
@@ -16,22 +16,38 @@
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>General</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="1">
<widget class="QComboBox" name="network_interface"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Network Interface</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>BCAT</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1" colspan="2">
<widget class="QLabel" name="label_2">
<property name="maximumSize">
<size>
<width>260</width>
<height>16777215</height>
</size>
</property>
<item row="3" column="0">
<widget class="QLabel" name="bcat_empty_header">
<property name="text">
<string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string>
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -51,11 +67,8 @@
</property>
</widget>
</item>
<item row="3" column="1" colspan="2">
<widget class="QLabel" name="bcat_empty_label">
<property name="enabled">
<bool>true</bool>
</property>
<item row="1" column="1" colspan="2">
<widget class="QLabel" name="label_2">
<property name="maximumSize">
<size>
<width>260</width>
@@ -63,10 +76,7 @@
</size>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
<string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
@@ -86,8 +96,17 @@
<item row="0" column="1" colspan="2">
<widget class="QComboBox" name="bcat_source"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="bcat_empty_header">
<item row="3" column="1" colspan="2">
<widget class="QLabel" name="bcat_empty_label">
<property name="enabled">
<bool>true</bool>
</property>
<property name="maximumSize">
<size>
<width>260</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>

View File

@@ -21,6 +21,7 @@ void ToggleConsole() {
console_shown = UISettings::values.show_console.GetValue();
}
using namespace Common::Log;
#if defined(_WIN32) && !defined(_DEBUG)
FILE* temp;
if (UISettings::values.show_console) {
@@ -29,24 +30,20 @@ void ToggleConsole() {
freopen_s(&temp, "CONIN$", "r", stdin);
freopen_s(&temp, "CONOUT$", "w", stdout);
freopen_s(&temp, "CONOUT$", "w", stderr);
Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>());
SetColorConsoleBackendEnabled(true);
}
} else {
if (FreeConsole()) {
// In order to close the console, we have to also detach the streams on it.
// Just redirect them to NUL if there is no console window
Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name());
SetColorConsoleBackendEnabled(false);
freopen_s(&temp, "NUL", "r", stdin);
freopen_s(&temp, "NUL", "w", stdout);
freopen_s(&temp, "NUL", "w", stderr);
}
}
#else
if (UISettings::values.show_console) {
Common::Log::AddBackend(std::make_unique<Common::Log::ColorConsoleBackend>());
} else {
Common::Log::RemoveBackend(Common::Log::ColorConsoleBackend::Name());
}
SetColorConsoleBackendEnabled(UISettings::values.show_console.GetValue());
#endif
}
} // namespace Debugger

View File

@@ -175,21 +175,6 @@ void GMainWindow::ShowTelemetryCallout() {
const int GMainWindow::max_recent_files_item;
static void InitializeLogging() {
using namespace Common;
Log::Filter log_filter;
log_filter.ParseFilterString(Settings::values.log_filter.GetValue());
Log::SetGlobalFilter(log_filter);
const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
void(FS::CreateDir(log_dir));
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
#endif
}
static void RemoveCachedContents() {
const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir);
const auto offline_fonts = cache_dir / "fonts";
@@ -207,8 +192,7 @@ GMainWindow::GMainWindow()
: input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
provider{std::make_unique<FileSys::ManualContentProvider>()} {
InitializeLogging();
Common::Log::Initialize();
LoadTranslation();
setAcceptDrops(true);
@@ -3437,6 +3421,7 @@ int main(int argc, char* argv[]) {
// generating shaders
setlocale(LC_ALL, "C");
Core::System::InitializeGlobalInstance();
GMainWindow main_window;
// After settings have been loaded by GMainWindow, apply the filter
main_window.show();

View File

@@ -74,31 +74,14 @@ static void PrintVersion() {
std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
}
static void InitializeLogging() {
using namespace Common;
Log::Filter log_filter(Log::Level::Debug);
log_filter.ParseFilterString(static_cast<std::string>(Settings::values.log_filter));
Log::SetGlobalFilter(log_filter);
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
const auto& log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir);
void(FS::CreateDir(log_dir));
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
#endif
}
/// Application entry point
int main(int argc, char** argv) {
Common::Log::Initialize();
Common::Log::SetColorConsoleBackendEnabled(true);
Common::DetachedTasks detached_tasks;
Config config;
int option_index = 0;
InitializeLogging();
#ifdef _WIN32
int argc_w;
auto argv_w = CommandLineToArgvW(GetCommandLineW(), &argc_w);
@@ -163,6 +146,7 @@ int main(int argc, char** argv) {
return -1;
}
Core::System::InitializeGlobalInstance();
auto& system{Core::System::GetInstance()};
InputCommon::InputSubsystem input_subsystem;