Compare commits
33 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7ca37b1e5 | ||
|
|
c20ea89390 | ||
|
|
790a09bc93 | ||
|
|
d10d480642 | ||
|
|
98c2f0e576 | ||
|
|
0c8594b225 | ||
|
|
3843995ceb | ||
|
|
de71a4d70d | ||
|
|
4654fb96b0 | ||
|
|
4d535799eb | ||
|
|
84b4ac5729 | ||
|
|
bed0c3c92a | ||
|
|
7eb4542c1d | ||
|
|
f65f8b9097 | ||
|
|
6fef91ce4c | ||
|
|
a428a843cb | ||
|
|
c4a1d3cbf4 | ||
|
|
bde6b899a1 | ||
|
|
455e28790a | ||
|
|
519978ce70 | ||
|
|
0821d95399 | ||
|
|
155b99ee55 | ||
|
|
ef2066b272 | ||
|
|
d82fc52cea | ||
|
|
151ab86204 | ||
|
|
4fd655cb46 | ||
|
|
9cb376f8c2 | ||
|
|
beb7305b73 | ||
|
|
14e93f133a | ||
|
|
af6290ed12 | ||
|
|
1770503185 | ||
|
|
81ed54d13e | ||
|
|
001675dced |
5
externals/CMakeLists.txt
vendored
5
externals/CMakeLists.txt
vendored
@@ -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()
|
||||
|
||||
|
||||
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 7946868af4...517e35f845
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <bitset>
|
||||
#include <initializer_list>
|
||||
#include <xbyak.h>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,6 +507,12 @@ 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();
|
||||
}
|
||||
|
||||
@@ -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,6 +205,9 @@ 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();
|
||||
|
||||
@@ -393,7 +396,7 @@ private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
|
||||
static System s_instance;
|
||||
inline static std::unique_ptr<System> s_instance{};
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -21,25 +21,34 @@ namespace Core {
|
||||
CpuManager::CpuManager(System& system_) : system{system_} {}
|
||||
CpuManager::~CpuManager() = default;
|
||||
|
||||
void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager,
|
||||
std::size_t core) {
|
||||
cpu_manager.RunThread(stop_token, core);
|
||||
void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
|
||||
cpu_manager.RunThread(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::jthread(ThreadStart, std::ref(*this), core);
|
||||
core_data[core].host_thread =
|
||||
std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
|
||||
}
|
||||
} else {
|
||||
core_data[0].host_thread = std::jthread(ThreadStart, std::ref(*this), 0);
|
||||
core_data[0].host_thread = std::make_unique<std::thread>(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() {
|
||||
@@ -118,18 +127,17 @@ void CpuManager::MultiCoreRunGuestLoop() {
|
||||
physical_core = &kernel.CurrentPhysicalCore();
|
||||
}
|
||||
system.ExitDynarmicProfile();
|
||||
{
|
||||
Kernel::KScopedDisableDispatch dd(kernel);
|
||||
physical_core->ArmInterface().ClearExclusiveState();
|
||||
}
|
||||
physical_core->ArmInterface().ClearExclusiveState();
|
||||
kernel.CurrentScheduler()->RescheduleCurrentCore();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::MultiCoreRunIdleThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
while (true) {
|
||||
Kernel::KScopedDisableDispatch dd(kernel);
|
||||
kernel.CurrentPhysicalCore().Idle();
|
||||
auto& physical_core = kernel.CurrentPhysicalCore();
|
||||
physical_core.Idle();
|
||||
kernel.CurrentScheduler()->RescheduleCurrentCore();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,12 +145,12 @@ void CpuManager::MultiCoreRunSuspendThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
while (true) {
|
||||
auto core = kernel.CurrentPhysicalCoreIndex();
|
||||
auto core = kernel.GetCurrentHostThreadID();
|
||||
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.CurrentPhysicalCoreIndex());
|
||||
ASSERT(core == kernel.GetCurrentHostThreadID());
|
||||
scheduler.RescheduleCurrentCore();
|
||||
}
|
||||
}
|
||||
@@ -309,7 +317,7 @@ void CpuManager::Pause(bool paused) {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||
void CpuManager::RunThread(std::size_t core) {
|
||||
/// Initialization
|
||||
system.RegisterCoreThread(core);
|
||||
std::string name;
|
||||
@@ -348,8 +356,8 @@ void CpuManager::RunThread(std::stop_token stop_token, std::size_t core) {
|
||||
sc_sync_first_use = false;
|
||||
}
|
||||
|
||||
// Emulation was stopped
|
||||
if (stop_token.stop_requested()) {
|
||||
// Abort if emulation was killed before the session really starts
|
||||
if (!system.IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,9 +78,9 @@ private:
|
||||
void SingleCoreRunSuspendThread();
|
||||
void SingleCorePause(bool paused);
|
||||
|
||||
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
|
||||
static void ThreadStart(CpuManager& cpu_manager, std::size_t core);
|
||||
|
||||
void RunThread(std::stop_token stop_token, std::size_t core);
|
||||
void RunThread(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::jthread host_thread;
|
||||
std::unique_ptr<std::thread> host_thread;
|
||||
};
|
||||
|
||||
std::atomic<bool> running_mode{};
|
||||
|
||||
@@ -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.Kernel().CurrentPhysicalCoreIndex();
|
||||
const auto current_core = system.CurrentCoreIndex();
|
||||
|
||||
// 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.Kernel().CurrentPhysicalCoreIndex();
|
||||
const auto current_core = system.CurrentCoreIndex();
|
||||
|
||||
// TODO(bunnei): We should disable interrupts here via KScopedInterruptDisable.
|
||||
// TODO(bunnei): We should call CanAccessAtomic(..) here.
|
||||
|
||||
@@ -170,10 +170,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& GetName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
private:
|
||||
void RegisterWithKernel();
|
||||
void UnregisterWithKernel();
|
||||
|
||||
@@ -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.Kernel().CurrentPhysicalCoreIndex();
|
||||
const auto current_core = system.CurrentCoreIndex();
|
||||
|
||||
// Load the value from the address.
|
||||
const auto expected = monitor.ExclusiveRead32(current_core, address);
|
||||
|
||||
@@ -13,7 +13,6 @@ 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);
|
||||
@@ -44,7 +43,6 @@ 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)) {
|
||||
@@ -63,7 +61,6 @@ 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.
|
||||
@@ -86,7 +83,6 @@ 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.
|
||||
@@ -97,7 +93,6 @@ ResultCode KHandleTable::Reserve(Handle* out_handle) {
|
||||
}
|
||||
|
||||
void KHandleTable::Unreserve(Handle handle) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
@@ -116,7 +111,6 @@ void KHandleTable::Unreserve(Handle handle) {
|
||||
}
|
||||
|
||||
void KHandleTable::Register(Handle handle, KAutoObject* obj, u16 type) {
|
||||
KScopedDisableDispatch dd(kernel);
|
||||
KScopedSpinLock lk(m_lock);
|
||||
|
||||
// Unpack the handle.
|
||||
|
||||
@@ -69,7 +69,6 @@ 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>) {
|
||||
@@ -124,7 +123,6 @@ 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.
|
||||
|
||||
@@ -59,7 +59,6 @@ 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
|
||||
|
||||
@@ -376,18 +376,20 @@ void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
|
||||
}
|
||||
|
||||
void KScheduler::DisableScheduling(KernelCore& kernel) {
|
||||
ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
|
||||
GetCurrentThreadPointer(kernel)->DisableDispatch();
|
||||
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
|
||||
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 0);
|
||||
scheduler->GetCurrentThread()->DisableDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
|
||||
ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 1);
|
||||
|
||||
if (GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() > 1) {
|
||||
GetCurrentThreadPointer(kernel)->EnableDispatch();
|
||||
} else {
|
||||
RescheduleCores(kernel, cores_needing_scheduling);
|
||||
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
|
||||
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
|
||||
if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
|
||||
scheduler->GetCurrentThread()->EnableDispatch();
|
||||
}
|
||||
}
|
||||
RescheduleCores(kernel, cores_needing_scheduling);
|
||||
}
|
||||
|
||||
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
|
||||
@@ -615,17 +617,13 @@ KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, c
|
||||
state.highest_priority_thread = nullptr;
|
||||
}
|
||||
|
||||
void KScheduler::Finalize() {
|
||||
KScheduler::~KScheduler() {
|
||||
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;
|
||||
@@ -644,12 +642,10 @@ void KScheduler::RescheduleCurrentCore() {
|
||||
if (phys_core.IsInterrupted()) {
|
||||
phys_core.ClearInterrupt();
|
||||
}
|
||||
|
||||
guard.Lock();
|
||||
if (state.needs_scheduling.load()) {
|
||||
Schedule();
|
||||
} else {
|
||||
GetCurrentThread()->EnableDispatch();
|
||||
guard.Unlock();
|
||||
}
|
||||
}
|
||||
@@ -659,33 +655,26 @@ void KScheduler::OnThreadStart() {
|
||||
}
|
||||
|
||||
void KScheduler::Unload(KThread* thread) {
|
||||
ASSERT(thread);
|
||||
|
||||
LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
|
||||
|
||||
if (thread->IsCallingSvc()) {
|
||||
thread->ClearIsCallingSvc();
|
||||
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();
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -694,6 +683,11 @@ 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());
|
||||
@@ -711,7 +705,7 @@ void KScheduler::SwitchContextStep2() {
|
||||
}
|
||||
|
||||
void KScheduler::ScheduleImpl() {
|
||||
KThread* previous_thread = GetCurrentThread();
|
||||
KThread* previous_thread = current_thread.load();
|
||||
KThread* next_thread = state.highest_priority_thread;
|
||||
|
||||
state.needs_scheduling = false;
|
||||
@@ -723,15 +717,10 @@ 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();
|
||||
@@ -742,7 +731,11 @@ void KScheduler::ScheduleImpl() {
|
||||
Unload(previous_thread);
|
||||
|
||||
std::shared_ptr<Common::Fiber>* old_context;
|
||||
old_context = &previous_thread->GetHostContext();
|
||||
if (previous_thread != nullptr) {
|
||||
old_context = &previous_thread->GetHostContext();
|
||||
} else {
|
||||
old_context = &idle_thread->GetHostContext();
|
||||
}
|
||||
guard.Unlock();
|
||||
|
||||
Common::Fiber::YieldTo(*old_context, *switch_fiber);
|
||||
|
||||
@@ -33,8 +33,6 @@ 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();
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#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"
|
||||
@@ -189,7 +188,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 = 0;
|
||||
sp.disable_count = 1;
|
||||
SetInExceptionHandler();
|
||||
|
||||
// Set thread ID.
|
||||
@@ -216,10 +215,9 @@ 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 emulation parameters.
|
||||
// Initialize host context.
|
||||
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;
|
||||
}
|
||||
@@ -972,9 +970,6 @@ ResultCode KThread::Run() {
|
||||
|
||||
// Set our state and finish.
|
||||
SetState(ThreadState::Runnable);
|
||||
|
||||
DisableDispatch();
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
}
|
||||
@@ -1059,16 +1054,4 @@ 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
|
||||
|
||||
@@ -450,39 +450,16 @@ 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--;
|
||||
}
|
||||
@@ -731,7 +708,6 @@ private:
|
||||
|
||||
// For emulation
|
||||
std::shared_ptr<Common::Fiber> host_context{};
|
||||
bool is_single_core{};
|
||||
|
||||
// For debugging
|
||||
std::vector<KSynchronizationObject*> wait_objects_for_debugging;
|
||||
@@ -776,16 +752,4 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class KScopedDisableDispatch {
|
||||
public:
|
||||
[[nodiscard]] explicit KScopedDisableDispatch(KernelCore& kernel_) : kernel{kernel_} {
|
||||
GetCurrentThread(kernel).DisableDispatch();
|
||||
}
|
||||
|
||||
~KScopedDisableDispatch();
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -85,9 +85,8 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
void InitializeCores() {
|
||||
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);
|
||||
for (auto& core : cores) {
|
||||
core.Initialize(current_process->Is64BitProcess());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +131,15 @@ 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();
|
||||
@@ -159,16 +167,6 @@ 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;
|
||||
|
||||
@@ -259,22 +257,33 @@ struct KernelCore::Impl {
|
||||
|
||||
void MakeCurrentProcess(KProcess* process) {
|
||||
current_process = process;
|
||||
}
|
||||
if (process == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/// 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++;
|
||||
const u32 core_id = GetCurrentHostThreadID();
|
||||
if (core_id < Core::Hardware::NUM_CPU_CORES) {
|
||||
system.Memory().SetCurrentPageTable(*process, core_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::optional<std::size_t> core_id = std::nullopt) {
|
||||
const thread_local auto host_thread_id{AllocateHostThreadId(core_id)};
|
||||
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() {
|
||||
if (host_thread_id == UINT32_MAX) {
|
||||
host_thread_id = next_host_thread_id++;
|
||||
}
|
||||
return host_thread_id;
|
||||
}
|
||||
|
||||
@@ -818,20 +827,16 @@ 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() {
|
||||
return impl->cores[CurrentPhysicalCoreIndex()];
|
||||
u32 core_id = impl->GetCurrentHostThreadID();
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
return impl->cores[core_id];
|
||||
}
|
||||
|
||||
const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
|
||||
return impl->cores[CurrentPhysicalCoreIndex()];
|
||||
u32 core_id = impl->GetCurrentHostThreadID();
|
||||
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
|
||||
return impl->cores[core_id];
|
||||
}
|
||||
|
||||
Kernel::KScheduler* KernelCore::CurrentScheduler() {
|
||||
@@ -1024,9 +1029,6 @@ 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1041,11 +1043,13 @@ void KernelCore::ExceptionalExit() {
|
||||
}
|
||||
|
||||
void KernelCore::EnterSVCProfile() {
|
||||
impl->svc_ticks[CurrentPhysicalCoreIndex()] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
|
||||
std::size_t core = impl->GetCurrentHostThreadID();
|
||||
impl->svc_ticks[core] = MicroProfileEnter(MICROPROFILE_TOKEN(Kernel_SVC));
|
||||
}
|
||||
|
||||
void KernelCore::ExitSVCProfile() {
|
||||
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
|
||||
std::size_t core = impl->GetCurrentHostThreadID();
|
||||
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
|
||||
}
|
||||
|
||||
std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
|
||||
|
||||
@@ -146,9 +146,6 @@ 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();
|
||||
|
||||
|
||||
@@ -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.Kernel().CurrentPhysicalCoreIndex()) {
|
||||
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
|
||||
out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,20 +9,17 @@
|
||||
#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_,
|
||||
KernelHelpers::ServiceContext& service_context_)
|
||||
: id(id_), layer_id(layer_id_), service_context{service_context_} {
|
||||
buffer_wait_event = service_context.CreateEvent("BufferQueue:WaitEvent");
|
||||
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() {
|
||||
service_context.CloseEvent(buffer_wait_event);
|
||||
}
|
||||
BufferQueue::~BufferQueue() = default;
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
|
||||
ASSERT(slot < buffer_slots);
|
||||
@@ -44,7 +41,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,
|
||||
@@ -122,7 +119,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() {
|
||||
@@ -157,7 +154,7 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
|
||||
}
|
||||
free_buffers_condition.notify_one();
|
||||
|
||||
buffer_wait_event->GetWritableEvent().Signal();
|
||||
buffer_wait_event.GetWritableEvent().Signal();
|
||||
}
|
||||
|
||||
void BufferQueue::Connect() {
|
||||
@@ -172,7 +169,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();
|
||||
}
|
||||
@@ -192,11 +189,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
|
||||
|
||||
@@ -24,10 +24,6 @@ class KReadableEvent;
|
||||
class KWritableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
namespace Service::KernelHelpers {
|
||||
class ServiceContext;
|
||||
} // namespace Service::KernelHelpers
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
constexpr u32 buffer_slots = 0x40;
|
||||
@@ -58,8 +54,7 @@ public:
|
||||
NativeWindowFormat = 2,
|
||||
};
|
||||
|
||||
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_,
|
||||
KernelHelpers::ServiceContext& service_context_);
|
||||
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id_, u64 layer_id_);
|
||||
~BufferQueue();
|
||||
|
||||
enum class BufferTransformFlags : u32 {
|
||||
@@ -135,14 +130,12 @@ 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
|
||||
|
||||
@@ -61,13 +61,12 @@ void NVFlinger::SplitVSync() {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
guard = std::make_shared<std::mutex>();
|
||||
|
||||
// Schedule the screen composition events
|
||||
@@ -147,7 +146,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, service_context));
|
||||
std::make_unique<BufferQueue>(system.Kernel(), buffer_queue_id, layer_id));
|
||||
display.CreateLayer(layer_id, *buffer_queues.back());
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/kernel_helpers.h"
|
||||
|
||||
namespace Common {
|
||||
class Event;
|
||||
@@ -136,8 +135,6 @@ 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
|
||||
|
||||
@@ -12,21 +12,18 @@
|
||||
#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_, 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(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() {
|
||||
service_context.CloseEvent(vsync_event);
|
||||
}
|
||||
Display::~Display() = default;
|
||||
|
||||
Layer& Display::GetLayer(std::size_t index) {
|
||||
return *layers.at(index);
|
||||
@@ -37,11 +34,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) {
|
||||
|
||||
@@ -18,9 +18,6 @@ class KEvent;
|
||||
namespace Service::NVFlinger {
|
||||
class BufferQueue;
|
||||
}
|
||||
namespace Service::KernelHelpers {
|
||||
class ServiceContext;
|
||||
} // namespace Service::KernelHelpers
|
||||
|
||||
namespace Service::VI {
|
||||
|
||||
@@ -34,13 +31,10 @@ class Display {
|
||||
public:
|
||||
/// Constructs a display with a given unique ID and name.
|
||||
///
|
||||
/// @param id The unique ID for this display.
|
||||
/// @param service_context_ The ServiceContext for the owning service.
|
||||
/// @param id The unique ID for this display.
|
||||
/// @param name_ The name for this display.
|
||||
/// @param system_ The global system instance.
|
||||
///
|
||||
Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_,
|
||||
Core::System& system_);
|
||||
Display(u64 id, std::string name_, Core::System& system);
|
||||
~Display();
|
||||
|
||||
/// Gets the unique ID assigned to this display.
|
||||
@@ -104,10 +98,9 @@ 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
|
||||
|
||||
@@ -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)};
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace Vulkan {
|
||||
// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines
|
||||
constexpr size_t SETS_GROW_RATE = 16;
|
||||
constexpr s32 SCORE_THRESHOLD = 3;
|
||||
constexpr u32 SETS_PER_POOL = 64;
|
||||
|
||||
struct DescriptorBank {
|
||||
DescriptorBankInfo info;
|
||||
@@ -58,11 +57,12 @@ static DescriptorBankInfo MakeBankInfo(std::span<const Shader::Info> infos) {
|
||||
static void AllocatePool(const Device& device, DescriptorBank& bank) {
|
||||
std::array<VkDescriptorPoolSize, 6> pool_sizes;
|
||||
size_t pool_cursor{};
|
||||
const u32 sets_per_pool = device.GetSetsPerPool();
|
||||
const auto add = [&](VkDescriptorType type, u32 count) {
|
||||
if (count > 0) {
|
||||
pool_sizes[pool_cursor++] = {
|
||||
.type = type,
|
||||
.descriptorCount = count * SETS_PER_POOL,
|
||||
.descriptorCount = count * sets_per_pool,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -77,7 +77,7 @@ static void AllocatePool(const Device& device, DescriptorBank& bank) {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
||||
.maxSets = SETS_PER_POOL,
|
||||
.maxSets = sets_per_pool,
|
||||
.poolSizeCount = static_cast<u32>(pool_cursor),
|
||||
.pPoolSizes = std::data(pool_sizes),
|
||||
}));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
@@ -596,6 +599,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
|
||||
graphics_queue = logical.GetQueue(graphics_family);
|
||||
present_queue = logical.GetQueue(present_family);
|
||||
|
||||
sets_per_pool = 64;
|
||||
if (driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE) {
|
||||
// AMD drivers need a higher amount of Sets per Pool in certain circunstances like in XC2.
|
||||
sets_per_pool = 96;
|
||||
}
|
||||
}
|
||||
|
||||
Device::~Device() = default;
|
||||
@@ -909,6 +918,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) {
|
||||
|
||||
@@ -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;
|
||||
@@ -313,6 +318,10 @@ public:
|
||||
return device_access_memory;
|
||||
}
|
||||
|
||||
u32 GetSetsPerPool() const {
|
||||
return sets_per_pool;
|
||||
}
|
||||
|
||||
private:
|
||||
/// Checks if the physical device is suitable.
|
||||
void CheckSuitability(bool requires_swapchain) const;
|
||||
@@ -366,8 +375,10 @@ private:
|
||||
VkShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.
|
||||
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
|
||||
u32 max_push_descriptors{}; ///< Maximum number of push descriptors
|
||||
u32 sets_per_pool{}; ///< Sets per Description Pool
|
||||
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.
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user