summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/logging/backend.cpp352
-rw-r--r--src/common/logging/backend.h113
-rw-r--r--src/common/logging/filter.cpp1
-rw-r--r--src/common/logging/types.h1
-rw-r--r--src/common/lru_cache.h140
-rw-r--r--src/common/settings.cpp2
-rw-r--r--src/common/settings.h6
-rw-r--r--src/common/threadsafe_queue.h10
-rw-r--r--src/common/x64/xbyak_abi.h2
-rw-r--r--src/common/x64/xbyak_util.h2
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/core.cpp17
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/cpu_manager.cpp25
-rw-r--r--src/core/cpu_manager.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp23
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp31
-rw-r--r--src/core/hle/service/ngct/ngct.cpp46
-rw-r--r--src/core/hle/service/ngct/ngct.h20
-rw-r--r--src/core/hle/service/nifm/nifm.cpp132
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp9
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp25
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h15
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp17
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h3
-rw-r--r--src/core/hle/service/service.cpp2
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp17
-rw-r--r--src/core/hle/service/vi/display/vi_display.h13
-rw-r--r--src/core/hle/service/vi/vi.cpp2
-rw-r--r--src/core/network/network.cpp64
-rw-r--r--src/core/network/network.h24
-rw-r--r--src/core/network/network_interface.cpp210
-rw-r--r--src/core/network/network_interface.h29
-rw-r--r--src/input_common/sdl/sdl_impl.cpp3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp56
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp60
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp127
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.h9
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp2
-rw-r--r--src/shader_recompiler/host_translate_info.h5
-rw-r--r--src/tests/common/param_package.cpp2
-rw-r--r--src/video_core/buffer_cache/buffer_base.h20
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h61
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp1
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h85
-rw-r--r--src/video_core/command_classes/vic.cpp5
-rw-r--r--src/video_core/engines/maxwell_3d.h8
-rw-r--r--src/video_core/macro/macro_jit_x64.h2
-rw-r--r--src/video_core/memory_manager.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_device.h4
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp6
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp4
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp11
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp40
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h6
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp30
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp5
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h14
-rw-r--r--src/video_core/texture_cache/image_base.h2
-rw-r--r--src/video_core/texture_cache/texture_cache.h92
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h8
-rw-r--r--src/video_core/textures/decoders.cpp181
-rw-r--r--src/video_core/textures/texture.h2
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp26
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h16
-rw-r--r--src/yuzu/CMakeLists.txt15
-rw-r--r--src/yuzu/applets/qt_software_keyboard.cpp2
-rw-r--r--src/yuzu/configuration/config.cpp8
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure.ui10
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_graphics.ui2
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp6
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.h1
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.ui14
-rw-r--r--src/yuzu/configuration/configure_input_player_widget.cpp34
-rw-r--r--src/yuzu/configuration/configure_network.cpp (renamed from src/yuzu/configuration/configure_service.cpp)39
-rw-r--r--src/yuzu/configuration/configure_network.h (renamed from src/yuzu/configuration/configure_service.h)10
-rw-r--r--src/yuzu/configuration/configure_network.ui (renamed from src/yuzu/configuration/configure_service.ui)63
-rw-r--r--src/yuzu/debugger/console.cpp11
-rw-r--r--src/yuzu/game_list.cpp8
-rw-r--r--src/yuzu/main.cpp19
-rw-r--r--src/yuzu_cmd/config.cpp1
-rw-r--r--src/yuzu_cmd/yuzu.cpp22
89 files changed, 1684 insertions, 872 deletions
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 61dddab3f..e40d117d6 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -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,104 +13,230 @@
#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/thread.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 {
+
/**
- * Static state as a singleton.
+ * Interface for logging backends.
*/
-class Impl {
+class Backend {
public:
- static Impl& Instance() {
- static Impl backend;
- return backend;
+ 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);
+ }
}
- Impl(const Impl&) = delete;
- Impl& operator=(const Impl&) = delete;
+ void Flush() override {
+ // stderr shouldn't be buffered
+ }
- Impl(Impl&&) = delete;
- Impl& operator=(Impl&&) = delete;
+ void EnableForStacktrace() override {
+ enabled = true;
+ }
- 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 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;
}
- void AddBackend(std::unique_ptr<Backend> backend) {
- std::lock_guard lock{writing_mutex};
- backends.push_back(std::move(backend));
+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 RemoveBackend(std::string_view backend_name) {
- std::lock_guard lock{writing_mutex};
+ void Flush() override {}
+
+ void EnableForStacktrace() override {}
+};
+
+bool initialization_in_progress_suppress_logging = true;
- std::erase_if(backends, [&backend_name](const auto& backend) {
- return backend_name == backend->GetName();
- });
+/**
+ * Static state as a singleton.
+ */
+class Impl {
+public:
+ static Impl& Instance() {
+ if (!instance) {
+ throw std::runtime_error("Using Logging instance before its initialization");
+ }
+ return *instance;
}
- const Filter& GetGlobalFilter() const {
- return filter;
+ 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;
+ Impl& operator=(const Impl&) = delete;
+
+ Impl(Impl&&) = delete;
+ Impl& operator=(Impl&&) = delete;
+
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);
- }
+ 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();
+ }
+ })} {}
- // 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() {
+ StopBackendThread();
}
- ~Impl() {
- Entry entry;
- entry.final_entry = true;
- message_queue.Push(entry);
+ void StopBackendThread() {
+ Entry stop_entry{};
+ stop_entry.final_entry = true;
+ message_queue.Push(stop_entry);
backend_thread.join();
}
@@ -135,100 +258,51 @@ private:
};
}
- std::mutex writing_mutex;
- std::thread backend_thread;
- std::vector<std::unique_ptr<Backend>> backends;
- MPSCQueue<Entry> message_queue;
- Filter filter;
- std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
-};
-
-ConsoleBackend::~ConsoleBackend() = default;
-
-void ConsoleBackend::Write(const Entry& entry) {
- PrintMessage(entry);
-}
-
-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 ForEachBackend(auto lambda) {
+ lambda(static_cast<Backend&>(debugger_backend));
+ lambda(static_cast<Backend&>(color_console_backend));
+ lambda(static_cast<Backend&>(file_backend));
+ }
-void FileBackend::Write(const Entry& entry) {
- if (!file->IsOpen()) {
- return;
+ static void Deleter(Impl* ptr) {
+ delete ptr;
}
- 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;
+ static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
- const bool write_limit_exceeded =
- bytes_written > MAX_BYTES_WRITTEN_EXTENDED ||
- (bytes_written > MAX_BYTES_WRITTEN && !Settings::values.extended_logging);
+ Filter filter;
+ DebuggerBackend debugger_backend{};
+ ColorConsoleBackend color_console_backend{};
+ FileBackend file_backend;
- // Close the file after the write limit is exceeded.
- if (write_limit_exceeded) {
- file->Close();
- return;
- }
+ std::thread backend_thread;
+ MPSCQueue<Entry> message_queue{};
+ std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
+};
+} // namespace
- bytes_written += file->WriteString(FormatLogMessage(entry).append(1, '\n'));
- if (entry.log_level >= Level::Error) {
- file->Flush();
- }
+void Initialize() {
+ Impl::Initialize();
}
-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
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 4b9a910c1..cb7839ee9 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -5,120 +5,21 @@
#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;
-
- 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;
-};
-
-/**
- * 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;
-};
+/// Initializes the logging system. This should be the first thing called in main.
+void Initialize();
-/**
- * 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);
+void DisableLoggingInTests();
/**
- * 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 \ No newline at end of file
+
+void SetColorConsoleBackendEnabled(bool enabled);
+} // namespace Common::Log
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index f055f0e11..42744c994 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -111,6 +111,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
SUB(Service, NCM) \
SUB(Service, NFC) \
SUB(Service, NFP) \
+ SUB(Service, NGCT) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NPNS) \
diff --git a/src/common/logging/types.h b/src/common/logging/types.h
index 7ad0334fc..ddf9d27ca 100644
--- a/src/common/logging/types.h
+++ b/src/common/logging/types.h
@@ -81,6 +81,7 @@ enum class Class : u8 {
Service_NCM, ///< The NCM service
Service_NFC, ///< The NFC (Near-field communication) service
Service_NFP, ///< The NFP service
+ Service_NGCT, ///< The NGCT (No Good Content for Terra) service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NPNS, ///< The NPNS service
diff --git a/src/common/lru_cache.h b/src/common/lru_cache.h
new file mode 100644
index 000000000..365488ba5
--- /dev/null
+++ b/src/common/lru_cache.h
@@ -0,0 +1,140 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2+ or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <deque>
+#include <memory>
+#include <type_traits>
+
+#include "common/common_types.h"
+
+namespace Common {
+
+template <class Traits>
+class LeastRecentlyUsedCache {
+ using ObjectType = typename Traits::ObjectType;
+ using TickType = typename Traits::TickType;
+
+ struct Item {
+ ObjectType obj;
+ TickType tick;
+ Item* next{};
+ Item* prev{};
+ };
+
+public:
+ LeastRecentlyUsedCache() : first_item{}, last_item{} {}
+ ~LeastRecentlyUsedCache() = default;
+
+ size_t Insert(ObjectType obj, TickType tick) {
+ const auto new_id = Build();
+ auto& item = item_pool[new_id];
+ item.obj = obj;
+ item.tick = tick;
+ Attach(item);
+ return new_id;
+ }
+
+ void Touch(size_t id, TickType tick) {
+ auto& item = item_pool[id];
+ if (item.tick >= tick) {
+ return;
+ }
+ item.tick = tick;
+ if (&item == last_item) {
+ return;
+ }
+ Detach(item);
+ Attach(item);
+ }
+
+ void Free(size_t id) {
+ auto& item = item_pool[id];
+ Detach(item);
+ item.prev = nullptr;
+ item.next = nullptr;
+ free_items.push_back(id);
+ }
+
+ template <typename Func>
+ void ForEachItemBelow(TickType tick, Func&& func) {
+ static constexpr bool RETURNS_BOOL =
+ std::is_same_v<std::invoke_result<Func, ObjectType>, bool>;
+ Item* iterator = first_item;
+ while (iterator) {
+ if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
+ return;
+ }
+ Item* next = iterator->next;
+ if constexpr (RETURNS_BOOL) {
+ if (func(iterator->obj)) {
+ return;
+ }
+ } else {
+ func(iterator->obj);
+ }
+ iterator = next;
+ }
+ }
+
+private:
+ size_t Build() {
+ if (free_items.empty()) {
+ const size_t item_id = item_pool.size();
+ auto& item = item_pool.emplace_back();
+ item.next = nullptr;
+ item.prev = nullptr;
+ return item_id;
+ }
+ const size_t item_id = free_items.front();
+ free_items.pop_front();
+ auto& item = item_pool[item_id];
+ item.next = nullptr;
+ item.prev = nullptr;
+ return item_id;
+ }
+
+ void Attach(Item& item) {
+ if (!first_item) {
+ first_item = &item;
+ }
+ if (!last_item) {
+ last_item = &item;
+ } else {
+ item.prev = last_item;
+ last_item->next = &item;
+ item.next = nullptr;
+ last_item = &item;
+ }
+ }
+
+ void Detach(Item& item) {
+ if (item.prev) {
+ item.prev->next = item.next;
+ }
+ if (item.next) {
+ item.next->prev = item.prev;
+ }
+ if (&item == first_item) {
+ first_item = item.next;
+ if (first_item) {
+ first_item->prev = nullptr;
+ }
+ }
+ if (&item == last_item) {
+ last_item = item.prev;
+ if (last_item) {
+ last_item->next = nullptr;
+ }
+ }
+ }
+
+ std::deque<Item> item_pool;
+ std::deque<size_t> free_items;
+ Item* first_item{};
+ Item* last_item{};
+};
+
+} // namespace Common
diff --git a/src/common/settings.cpp b/src/common/settings.cpp
index 33665eab8..0d2df80a8 100644
--- a/src/common/settings.cpp
+++ b/src/common/settings.cpp
@@ -59,7 +59,6 @@ void LogSettings() {
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_ShaderBackend", values.shader_backend.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
- log_setting("Renderer_UseGarbageCollection", values.use_caches_gc.GetValue());
log_setting("Renderer_AnisotropicFilteringLevel", values.max_anisotropy.GetValue());
log_setting("Audio_OutputEngine", values.sink_id.GetValue());
log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue());
@@ -143,7 +142,6 @@ void RestoreGlobalState(bool is_powered_on) {
values.shader_backend.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
values.use_fast_gpu_time.SetGlobal(true);
- values.use_caches_gc.SetGlobal(true);
values.bg_red.SetGlobal(true);
values.bg_green.SetGlobal(true);
values.bg_blue.SetGlobal(true);
diff --git a/src/common/settings.h b/src/common/settings.h
index 28074c1b9..b7195670b 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -481,7 +481,6 @@ struct Values {
ShaderBackend::SPIRV, "shader_backend"};
Setting<bool> use_asynchronous_shaders{false, "use_asynchronous_shaders"};
Setting<bool> use_fast_gpu_time{true, "use_fast_gpu_time"};
- Setting<bool> use_caches_gc{false, "use_caches_gc"};
Setting<u8> bg_red{0, "bg_red"};
Setting<u8> bg_green{0, "bg_green"};
@@ -495,7 +494,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"};
@@ -564,9 +563,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"};
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index ad04df8ca..8430b9778 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -46,15 +46,13 @@ public:
ElementPtr* new_ptr = new ElementPtr();
write_ptr->next.store(new_ptr, std::memory_order_release);
write_ptr = new_ptr;
+ ++size;
- const size_t previous_size{size++};
-
- // Acquire the mutex and then immediately release it as a fence.
+ // cv_mutex must be held or else there will be a missed wakeup if the other thread is in the
+ // line before cv.wait
// TODO(bunnei): This can be replaced with C++20 waitable atomics when properly supported.
// See discussion on https://github.com/yuzu-emu/yuzu/pull/3173 for details.
- if (previous_size == 0) {
- std::lock_guard lock{cv_mutex};
- }
+ std::lock_guard lock{cv_mutex};
cv.notify_one();
}
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index c2c9b6134..0ddf9b83e 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -6,7 +6,7 @@
#include <bitset>
#include <initializer_list>
-#include <xbyak.h>
+#include <xbyak/xbyak.h>
#include "common/assert.h"
namespace Common::X64 {
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h
index df17f8cbe..44d2558f1 100644
--- a/src/common/x64/xbyak_util.h
+++ b/src/common/x64/xbyak_util.h
@@ -5,7 +5,7 @@
#pragma once
#include <type_traits>
-#include <xbyak.h>
+#include <xbyak/xbyak.h>
#include "common/x64/xbyak_abi.h"
namespace Common::X64 {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 5c99c00f5..87d47e2e5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -452,6 +452,8 @@ add_library(core STATIC
hle/service/nfp/nfp.h
hle/service/nfp/nfp_user.cpp
hle/service/nfp/nfp_user.h
+ hle/service/ngct/ngct.cpp
+ hle/service/ngct/ngct.h
hle/service/nifm/nifm.cpp
hle/service/nifm/nifm.h
hle/service/nim/nim.cpp
@@ -636,6 +638,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
diff --git a/src/core/core.cpp b/src/core/core.cpp
index d3e84c4ef..ba4629993 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -4,6 +4,7 @@
#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;
}
diff --git a/src/core/core.h b/src/core/core.h
index ea143043c..715ab88e7 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -120,9 +120,9 @@ public:
* 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 {
@@ -396,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
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 7e195346b..77efcabf0 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -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() {
@@ -317,7 +308,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;
@@ -361,6 +352,10 @@ void CpuManager::RunThread(std::size_t core) {
return;
}
+ if (stop_token.stop_requested()) {
+ break;
+ }
+
auto current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
data.is_running = true;
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 140263b09..9d92d4af0 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -78,9 +78,9 @@ private:
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{};
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 92fbc5532..bea945301 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -267,20 +267,23 @@ struct KernelCore::Impl {
}
}
- /// 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;
}
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 2eb532472..62fb06c45 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -1078,8 +1078,8 @@ static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Hand
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.
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index ef6854d62..36a4aa9cd 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -16,6 +16,30 @@
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
diff --git a/src/core/hle/service/ngct/ngct.cpp b/src/core/hle/service/ngct/ngct.cpp
new file mode 100644
index 000000000..deb3abb28
--- /dev/null
+++ b/src/core/hle/service/ngct/ngct.cpp
@@ -0,0 +1,46 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/ngct/ngct.h"
+#include "core/hle/service/service.h"
+
+namespace Service::NGCT {
+
+class IService final : public ServiceFramework<IService> {
+public:
+ explicit IService(Core::System& system_) : ServiceFramework{system_, "ngct:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "Match"},
+ {1, &IService::Filter, "Filter"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void Filter(Kernel::HLERequestContext& ctx) {
+ const auto buffer = ctx.ReadBuffer();
+ const auto text = Common::StringFromFixedZeroTerminatedBuffer(
+ reinterpret_cast<const char*>(buffer.data()), buffer.size());
+
+ LOG_WARNING(Service_NGCT, "(STUBBED) called, text={}", text);
+
+ // Return the same string since we don't censor anything
+ ctx.WriteBuffer(buffer);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+};
+
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
+ std::make_shared<IService>(system)->InstallAsService(system.ServiceManager());
+}
+
+} // namespace Service::NGCT
diff --git a/src/core/hle/service/ngct/ngct.h b/src/core/hle/service/ngct/ngct.h
new file mode 100644
index 000000000..1f2a47b78
--- /dev/null
+++ b/src/core/hle/service/ngct/ngct.h
@@ -0,0 +1,20 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#pragma once
+
+namespace Core {
+class System;
+}
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::NGCT {
+
+/// Registers all NGCT services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
+
+} // namespace Service::NGCT
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index e742db48f..9decb9290 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -11,6 +11,7 @@
#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);
}
}
@@ -276,37 +277,45 @@ private:
void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
- const SfNetworkProfileData network_profile_data{
- .ip_setting_data{
- .ip_address_setting{
- .is_automatic{true},
- .current_address{192, 168, 1, 100},
- .subnet_mask{255, 255, 255, 0},
- .gateway{192, 168, 1, 1},
- },
- .dns_setting{
- .is_automatic{true},
- .primary_dns{1, 1, 1, 1},
- .secondary_dns{1, 0, 0, 1},
+ const auto net_iface = Network::GetSelectedNetworkInterface();
+
+ const SfNetworkProfileData network_profile_data = [&net_iface] {
+ if (!net_iface) {
+ return SfNetworkProfileData{};
+ }
+
+ return SfNetworkProfileData{
+ .ip_setting_data{
+ .ip_address_setting{
+ .is_automatic{true},
+ .current_address{Network::TranslateIPv4(net_iface->ip_address)},
+ .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
+ .gateway{Network::TranslateIPv4(net_iface->gateway)},
+ },
+ .dns_setting{
+ .is_automatic{true},
+ .primary_dns{1, 1, 1, 1},
+ .secondary_dns{1, 0, 0, 1},
+ },
+ .proxy_setting{
+ .enabled{false},
+ .port{},
+ .proxy_server{},
+ .automatic_auth_enabled{},
+ .user{},
+ .password{},
+ },
+ .mtu{1500},
},
- .proxy_setting{
- .enabled{false},
- .port{},
- .proxy_server{},
- .automatic_auth_enabled{},
- .user{},
- .password{},
+ .uuid{0xdeadbeef, 0xdeadbeef},
+ .network_name{"yuzu Network"},
+ .wireless_setting_data{
+ .ssid_length{12},
+ .ssid{"yuzu Network"},
+ .passphrase{"yuzupassword"},
},
- .mtu{1500},
- },
- .uuid{0xdeadbeef, 0xdeadbeef},
- .network_name{"yuzu Network"},
- .wireless_setting_data{
- .ssid_length{12},
- .ssid{"yuzu Network"},
- .passphrase{"yuzupassword"},
- },
- };
+ };
+ }();
ctx.WriteBuffer(network_profile_data);
@@ -322,12 +331,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");
@@ -348,25 +360,33 @@ private:
LOG_WARNING(Service_NIFM, "(STUBBED) called");
struct IpConfigInfo {
- IpAddressSetting ip_address_setting;
- DnsSetting dns_setting;
+ IpAddressSetting ip_address_setting{};
+ DnsSetting dns_setting{};
};
static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting),
"IpConfigInfo has incorrect size.");
- const IpConfigInfo ip_config_info{
- .ip_address_setting{
- .is_automatic{true},
- .current_address{192, 168, 1, 100},
- .subnet_mask{255, 255, 255, 0},
- .gateway{192, 168, 1, 1},
- },
- .dns_setting{
- .is_automatic{true},
- .primary_dns{1, 1, 1, 1},
- .secondary_dns{1, 0, 0, 1},
- },
- };
+ const auto net_iface = Network::GetSelectedNetworkInterface();
+
+ const IpConfigInfo ip_config_info = [&net_iface] {
+ if (!net_iface) {
+ return IpConfigInfo{};
+ }
+
+ return IpConfigInfo{
+ .ip_address_setting{
+ .is_automatic{true},
+ .current_address{Network::TranslateIPv4(net_iface->ip_address)},
+ .subnet_mask{Network::TranslateIPv4(net_iface->subnet_mask)},
+ .gateway{Network::TranslateIPv4(net_iface->gateway)},
+ },
+ .dns_setting{
+ .is_automatic{true},
+ .primary_dns{1, 1, 1, 1},
+ .secondary_dns{1, 0, 0, 1},
+ },
+ };
+ }();
IPC::ResponseBuilder rb{ctx, 2 + (sizeof(IpConfigInfo) + 3) / sizeof(u32)};
rb.Push(ResultSuccess);
@@ -384,10 +404,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 +415,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);
}
}
};
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index ce6065db2..a33e47d0b 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -42,15 +42,14 @@ void nvdisp_disp0::OnClose(DeviceFD fd) {}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
u32 stride, NVFlinger::BufferQueue::BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect) {
- VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
+ const VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
LOG_TRACE(Service,
"Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}",
addr, offset, width, height, stride, format);
- using PixelFormat = Tegra::FramebufferConfig::PixelFormat;
- const Tegra::FramebufferConfig framebuffer{
- addr, offset, width, height, stride, static_cast<PixelFormat>(format),
- transform, crop_rect};
+ const auto pixel_format = static_cast<Tegra::FramebufferConfig::PixelFormat>(format);
+ const Tegra::FramebufferConfig framebuffer{addr, offset, width, height,
+ stride, pixel_format, transform, crop_rect};
system.GetPerfStats().EndSystemFrame();
system.GPU().SwapBuffers(&framebuffer);
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 59ddf6298..b4c3a6099 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -9,17 +9,20 @@
#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
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 61e337ac5..78de3f354 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -24,6 +24,10 @@ class KReadableEvent;
class KWritableEvent;
} // namespace Kernel
+namespace Service::KernelHelpers {
+class ServiceContext;
+} // namespace Service::KernelHelpers
+
namespace Service::NVFlinger {
constexpr u32 buffer_slots = 0x40;
@@ -38,7 +42,9 @@ struct IGBPBuffer {
u32_le index;
INSERT_PADDING_WORDS(3);
u32_le gpu_buffer_id;
- INSERT_PADDING_WORDS(17);
+ INSERT_PADDING_WORDS(6);
+ u32_le external_format;
+ INSERT_PADDING_WORDS(10);
u32_le nvmap_handle;
u32_le offset;
INSERT_PADDING_WORDS(60);
@@ -54,7 +60,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 +137,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
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 941748970..3ead813b0 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -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());
}
@@ -297,7 +298,7 @@ void NVFlinger::Compose() {
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
ASSERT(nvdisp);
- nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
+ nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.external_format,
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
buffer->get().transform, buffer->get().crop_rect);
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index d80fd07ef..6d84cafb4 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -15,6 +15,7 @@
#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
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index b3e50433b..065133166 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -46,6 +46,7 @@
#include "core/hle/service/ncm/ncm.h"
#include "core/hle/service/nfc/nfc.h"
#include "core/hle/service/nfp/nfp.h"
+#include "core/hle/service/ngct/ngct.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/nim/nim.h"
#include "core/hle/service/npns/npns.h"
@@ -271,6 +272,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
NCM::InstallInterfaces(*sm, system);
NFC::InstallInterfaces(*sm, system);
NFP::InstallInterfaces(*sm, system);
+ NGCT::InstallInterfaces(*sm, system);
NIFM::InstallInterfaces(*sm, system);
NIM::InstallInterfaces(*sm, system);
NPNS::InstallInterfaces(*sm, system);
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index 0dd342dbf..b7705c02a 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -12,18 +12,21 @@
#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) {
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 166f2a4cc..0979fc421 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -18,6 +18,9 @@ class KEvent;
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
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 3e5949d52..8e8fc40ca 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -1158,7 +1158,7 @@ private:
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;
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 375bc79ec..4732d4485 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -10,9 +10,10 @@
#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;
+ });
+
+ 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);
- in_addr addr;
- std::memcpy(&addr, ent->h_addr_list[0], sizeof(addr));
- return {TranslateIPv4(addr), Errno::SUCCESS};
+ 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) {
diff --git a/src/core/network/network.h b/src/core/network/network.h
index bd30f1899..fdd3e4655 100644
--- a/src/core/network/network.h
+++ b/src/core/network/network.h
@@ -5,11 +5,18 @@
#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
diff --git a/src/core/network/network_interface.cpp b/src/core/network/network_interface.cpp
new file mode 100644
index 000000000..6811f21b1
--- /dev/null
+++ b/src/core/network/network_interface.cpp
@@ -0,0 +1,210 @@
+// 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) {
+ break;
+ }
+
+ adapter_addresses.resize((buf_size / sizeof(IP_ADAPTER_ADDRESSES)) + 1);
+ }
+
+ if (ret != NO_ERROR) {
+ LOG_ERROR(Network, "Failed to get network interfaces with GetAdaptersAddresses");
+ return {};
+ }
+
+ 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.emplace_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
+
+std::vector<NetworkInterface> GetAvailableNetworkInterfaces() {
+ struct ifaddrs* ifaddr = nullptr;
+
+ if (getifaddrs(&ifaddr) != 0) {
+ LOG_ERROR(Network, "Failed to get network interfaces with getifaddrs: {}",
+ std::strerror(errno));
+ return {};
+ }
+
+ std::vector<NetworkInterface> 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;
+ }
+
+ u32 gateway{};
+
+ std::ifstream file{"/proc/net/route"};
+ if (!file.is_open()) {
+ LOG_ERROR(Network, "Failed to open \"/proc/net/route\"");
+
+ result.emplace_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}}});
+ continue;
+ }
+
+ // 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;
+
+ u32 dest{};
+ iss >> dest;
+ if (dest != 0) {
+ // not the default route
+ continue;
+ }
+
+ iss >> gateway;
+
+ u16 flags{};
+ iss >> flags;
+
+ // flag RTF_GATEWAY (defined in <linux/route.h>)
+ if ((flags & 0x2) == 0) {
+ continue;
+ }
+
+ gateway_found = true;
+ break;
+ }
+
+ if (!gateway_found) {
+ gateway = 0;
+ }
+
+ result.emplace_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 auto& 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 std::nullopt;
+ }
+
+ 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()) {
+ LOG_ERROR(Network, "Couldn't find selected interface \"{}\"", selected_network_interface);
+ return std::nullopt;
+ }
+
+ return *res;
+}
+
+} // namespace Network
diff --git a/src/core/network/network_interface.h b/src/core/network/network_interface.h
new file mode 100644
index 000000000..980edb2f5
--- /dev/null
+++ b/src/core/network/network_interface.h
@@ -0,0 +1,29 @@
+// 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
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index f1f950d8a..f102410d1 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -889,6 +889,9 @@ SDLState::SDLState() {
RegisterFactory<VibrationDevice>("sdl", vibration_factory);
RegisterFactory<MotionDevice>("sdl", motion_factory);
+ // Disable raw input. When enabled this setting causes SDL to die when a web applet opens
+ SDL_SetHint(SDL_HINT_JOYSTICK_RAWINPUT, "0");
+
// Enable HIDAPI rumble. This prevents SDL from disabling motion on PS4 and PS5 controllers
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 2d29d8c14..2885e6799 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -15,6 +15,8 @@
namespace Shader::Backend::SPIRV {
namespace {
+constexpr size_t NUM_FIXEDFNCTEXTURE = 10;
+
enum class Operation {
Increment,
Decrement,
@@ -427,6 +429,16 @@ Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
return pointer_type;
}
}
+
+size_t FindNextUnusedLocation(const std::bitset<IR::NUM_GENERICS>& used_locations,
+ size_t start_offset) {
+ for (size_t location = start_offset; location < used_locations.size(); ++location) {
+ if (!used_locations.test(location)) {
+ return location;
+ }
+ }
+ throw RuntimeError("Unable to get an unused location for legacy attribute");
+}
} // Anonymous namespace
void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
@@ -1227,6 +1239,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
loads[IR::Attribute::TessellationEvaluationPointV]) {
tess_coord = DefineInput(*this, F32[3], false, spv::BuiltIn::TessCoord);
}
+ std::bitset<IR::NUM_GENERICS> used_locations{};
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
const AttributeType input_type{runtime_info.generic_input_types[index]};
if (!runtime_info.previous_stage_stores.Generic(index)) {
@@ -1238,6 +1251,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
if (input_type == AttributeType::Disabled) {
continue;
}
+ used_locations.set(index);
const Id type{GetAttributeType(*this, input_type)};
const Id id{DefineInput(*this, type, true)};
Decorate(id, spv::Decoration::Location, static_cast<u32>(index));
@@ -1263,6 +1277,26 @@ void EmitContext::DefineInputs(const IR::Program& program) {
break;
}
}
+ size_t previous_unused_location = 0;
+ if (loads.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
+ const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location);
+ previous_unused_location = location;
+ used_locations.set(location);
+ const Id id{DefineInput(*this, F32[4], true)};
+ Decorate(id, spv::Decoration::Location, location);
+ input_front_color = id;
+ }
+ for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
+ if (loads.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
+ const size_t location =
+ FindNextUnusedLocation(used_locations, previous_unused_location);
+ previous_unused_location = location;
+ used_locations.set(location);
+ const Id id{DefineInput(*this, F32[4], true)};
+ Decorate(id, spv::Decoration::Location, location);
+ input_fixed_fnc_textures[index] = id;
+ }
+ }
if (stage == Stage::TessellationEval) {
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
if (!info.uses_patches[index]) {
@@ -1313,9 +1347,31 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
viewport_mask = DefineOutput(*this, TypeArray(U32[1], Const(1u)), std::nullopt,
spv::BuiltIn::ViewportMaskNV);
}
+ std::bitset<IR::NUM_GENERICS> used_locations{};
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
if (info.stores.Generic(index)) {
DefineGenericOutput(*this, index, invocations);
+ used_locations.set(index);
+ }
+ }
+ size_t previous_unused_location = 0;
+ if (info.stores.AnyComponent(IR::Attribute::ColorFrontDiffuseR)) {
+ const size_t location = FindNextUnusedLocation(used_locations, previous_unused_location);
+ previous_unused_location = location;
+ used_locations.set(location);
+ const Id id{DefineOutput(*this, F32[4], invocations)};
+ Decorate(id, spv::Decoration::Location, static_cast<u32>(location));
+ output_front_color = id;
+ }
+ for (size_t index = 0; index < NUM_FIXEDFNCTEXTURE; ++index) {
+ if (info.stores.AnyComponent(IR::Attribute::FixedFncTexture0S + index * 4)) {
+ const size_t location =
+ FindNextUnusedLocation(used_locations, previous_unused_location);
+ previous_unused_location = location;
+ used_locations.set(location);
+ const Id id{DefineOutput(*this, F32[4], invocations)};
+ Decorate(id, spv::Decoration::Location, location);
+ output_fixed_fnc_textures[index] = id;
}
}
switch (stage) {
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index e277bc358..847d0c0e6 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -268,10 +268,14 @@ public:
Id write_global_func_u32x4{};
Id input_position{};
+ Id input_front_color{};
+ std::array<Id, 10> input_fixed_fnc_textures{};
std::array<Id, 32> input_generics{};
Id output_point_size{};
Id output_position{};
+ Id output_front_color{};
+ std::array<Id, 10> output_fixed_fnc_textures{};
std::array<std::array<GenericElementInfo, 4>, 32> output_generics{};
Id output_tess_level_outer{};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index fb8c02a77..68f360b3c 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -43,6 +43,25 @@ Id AttrPointer(EmitContext& ctx, Id pointer_type, Id vertex, Id base, Args&&...
}
}
+bool IsFixedFncTexture(IR::Attribute attribute) {
+ return attribute >= IR::Attribute::FixedFncTexture0S &&
+ attribute <= IR::Attribute::FixedFncTexture9Q;
+}
+
+u32 FixedFncTextureAttributeIndex(IR::Attribute attribute) {
+ if (!IsFixedFncTexture(attribute)) {
+ throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute);
+ }
+ return (static_cast<u32>(attribute) - static_cast<u32>(IR::Attribute::FixedFncTexture0S)) / 4u;
+}
+
+u32 FixedFncTextureAttributeElement(IR::Attribute attribute) {
+ if (!IsFixedFncTexture(attribute)) {
+ throw InvalidArgument("Attribute {} is not a FixedFncTexture", attribute);
+ }
+ return static_cast<u32>(attribute) % 4u;
+}
+
template <typename... Args>
Id OutputAccessChain(EmitContext& ctx, Id result_type, Id base, Args&&... args) {
if (ctx.stage == Stage::TessellationControl) {
@@ -74,6 +93,13 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
return OutputAccessChain(ctx, ctx.output_f32, info.id, index_id);
}
}
+ if (IsFixedFncTexture(attr)) {
+ const u32 index{FixedFncTextureAttributeIndex(attr)};
+ const u32 element{FixedFncTextureAttributeElement(attr)};
+ const Id element_id{ctx.Const(element)};
+ return OutputAccessChain(ctx, ctx.output_f32, ctx.output_fixed_fnc_textures[index],
+ element_id);
+ }
switch (attr) {
case IR::Attribute::PointSize:
return ctx.output_point_size;
@@ -85,6 +111,14 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
const Id element_id{ctx.Const(element)};
return OutputAccessChain(ctx, ctx.output_f32, ctx.output_position, element_id);
}
+ case IR::Attribute::ColorFrontDiffuseR:
+ case IR::Attribute::ColorFrontDiffuseG:
+ case IR::Attribute::ColorFrontDiffuseB:
+ case IR::Attribute::ColorFrontDiffuseA: {
+ const u32 element{static_cast<u32>(attr) % 4};
+ const Id element_id{ctx.Const(element)};
+ return OutputAccessChain(ctx, ctx.output_f32, ctx.output_front_color, element_id);
+ }
case IR::Attribute::ClipDistance0:
case IR::Attribute::ClipDistance1:
case IR::Attribute::ClipDistance2:
@@ -298,19 +332,21 @@ 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)};
return type->needs_cast ? ctx.OpBitcast(ctx.F32[1], value) : value;
}
+ if (IsFixedFncTexture(attr)) {
+ const u32 index{FixedFncTextureAttributeIndex(attr)};
+ const Id attr_id{ctx.input_fixed_fnc_textures[index]};
+ const Id attr_ptr{AttrPointer(ctx, ctx.input_f32, vertex, attr_id, ctx.Const(element))};
+ return ctx.OpLoad(ctx.F32[1], attr_ptr);
+ }
switch (attr) {
case IR::Attribute::PrimitiveId:
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.primitive_id));
@@ -320,6 +356,13 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
case IR::Attribute::PositionW:
return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_position,
ctx.Const(element)));
+ case IR::Attribute::ColorFrontDiffuseR:
+ case IR::Attribute::ColorFrontDiffuseG:
+ case IR::Attribute::ColorFrontDiffuseB:
+ case IR::Attribute::ColorFrontDiffuseA: {
+ return ctx.OpLoad(ctx.F32[1], AttrPointer(ctx, ctx.input_f32, vertex, ctx.input_front_color,
+ ctx.Const(element)));
+ }
case IR::Attribute::InstanceId:
if (ctx.profile.support_vertex_instance_id) {
return ctx.OpBitcast(ctx.F32[1], ctx.OpLoad(ctx.U32[1], ctx.instance_id));
@@ -337,8 +380,9 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
return ctx.OpBitcast(ctx.F32[1], ctx.OpISub(ctx.U32[1], index, base));
}
case IR::Attribute::FrontFace:
- return ctx.OpSelect(ctx.U32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
- ctx.Const(std::numeric_limits<u32>::max()), ctx.u32_zero_value);
+ return ctx.OpSelect(ctx.F32[1], ctx.OpLoad(ctx.U1, ctx.front_face),
+ ctx.OpBitcast(ctx.F32[1], ctx.Const(std::numeric_limits<u32>::max())),
+ ctx.f32_zero_value);
case IR::Attribute::PointSpriteS:
return ctx.OpLoad(ctx.F32[1],
ctx.OpAccessChain(ctx.input_f32, ctx.point_coord, ctx.u32_zero_value));
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
index 8b3e0a15c..69eeaa3e6 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -20,6 +20,7 @@
#include "shader_recompiler/frontend/maxwell/decode.h"
#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
#include "shader_recompiler/frontend/maxwell/translate/translate.h"
+#include "shader_recompiler/host_translate_info.h"
#include "shader_recompiler/object_pool.h"
namespace Shader::Maxwell {
@@ -652,7 +653,7 @@ class TranslatePass {
public:
TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
- IR::AbstractSyntaxList& syntax_list_)
+ IR::AbstractSyntaxList& syntax_list_, const HostTranslateInfo& host_info)
: stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
syntax_list{syntax_list_} {
Visit(root_stmt, nullptr, nullptr);
@@ -660,6 +661,9 @@ public:
IR::Block& first_block{*syntax_list.front().data.block};
IR::IREmitter ir(first_block, first_block.begin());
ir.Prologue();
+ if (uses_demote_to_helper && host_info.needs_demote_reorder) {
+ DemoteCombinationPass();
+ }
}
private:
@@ -809,7 +813,14 @@ private:
}
case StatementType::Return: {
ensure_block();
- IR::IREmitter{*current_block}.Epilogue();
+ IR::Block* return_block{block_pool.Create(inst_pool)};
+ IR::IREmitter{*return_block}.Epilogue();
+ current_block->AddBranch(return_block);
+
+ auto& merge{syntax_list.emplace_back()};
+ merge.type = IR::AbstractSyntaxNode::Type::Block;
+ merge.data.block = return_block;
+
current_block = nullptr;
syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return;
break;
@@ -824,6 +835,7 @@ private:
auto& merge{syntax_list.emplace_back()};
merge.type = IR::AbstractSyntaxNode::Type::Block;
merge.data.block = demote_block;
+ uses_demote_to_helper = true;
break;
}
case StatementType::Unreachable: {
@@ -855,11 +867,117 @@ private:
return block_pool.Create(inst_pool);
}
+ void DemoteCombinationPass() {
+ using Type = IR::AbstractSyntaxNode::Type;
+ std::vector<IR::Block*> demote_blocks;
+ std::vector<IR::U1> demote_conds;
+ u32 num_epilogues{};
+ u32 branch_depth{};
+ for (const IR::AbstractSyntaxNode& node : syntax_list) {
+ if (node.type == Type::If) {
+ ++branch_depth;
+ }
+ if (node.type == Type::EndIf) {
+ --branch_depth;
+ }
+ if (node.type != Type::Block) {
+ continue;
+ }
+ if (branch_depth > 1) {
+ // Skip reordering nested demote branches.
+ continue;
+ }
+ for (const IR::Inst& inst : node.data.block->Instructions()) {
+ const IR::Opcode op{inst.GetOpcode()};
+ if (op == IR::Opcode::DemoteToHelperInvocation) {
+ demote_blocks.push_back(node.data.block);
+ break;
+ }
+ if (op == IR::Opcode::Epilogue) {
+ ++num_epilogues;
+ }
+ }
+ }
+ if (demote_blocks.size() == 0) {
+ return;
+ }
+ if (num_epilogues > 1) {
+ LOG_DEBUG(Shader, "Combining demotes with more than one return is not implemented.");
+ return;
+ }
+ s64 last_iterator_offset{};
+ auto& asl{syntax_list};
+ for (const IR::Block* demote_block : demote_blocks) {
+ const auto start_it{asl.begin() + last_iterator_offset};
+ auto asl_it{std::find_if(start_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
+ return asn.type == Type::If && asn.data.if_node.body == demote_block;
+ })};
+ if (asl_it == asl.end()) {
+ // Demote without a conditional branch.
+ // No need to proceed since all fragment instances will be demoted regardless.
+ return;
+ }
+ const IR::Block* const end_if = asl_it->data.if_node.merge;
+ demote_conds.push_back(asl_it->data.if_node.cond);
+ last_iterator_offset = std::distance(asl.begin(), asl_it);
+
+ asl_it = asl.erase(asl_it);
+ asl_it = std::find_if(asl_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
+ return asn.type == Type::Block && asn.data.block == demote_block;
+ });
+
+ asl_it = asl.erase(asl_it);
+ asl_it = std::find_if(asl_it, asl.end(), [&](const IR::AbstractSyntaxNode& asn) {
+ return asn.type == Type::EndIf && asn.data.end_if.merge == end_if;
+ });
+ asl_it = asl.erase(asl_it);
+ }
+ const auto epilogue_func{[](const IR::AbstractSyntaxNode& asn) {
+ if (asn.type != Type::Block) {
+ return false;
+ }
+ for (const auto& inst : asn.data.block->Instructions()) {
+ if (inst.GetOpcode() == IR::Opcode::Epilogue) {
+ return true;
+ }
+ }
+ return false;
+ }};
+ const auto reverse_it{std::find_if(asl.rbegin(), asl.rend(), epilogue_func)};
+ const auto return_block_it{(reverse_it + 1).base()};
+
+ IR::IREmitter ir{*(return_block_it - 1)->data.block};
+ IR::U1 cond(IR::Value(false));
+ for (const auto& demote_cond : demote_conds) {
+ cond = ir.LogicalOr(cond, demote_cond);
+ }
+ cond.Inst()->DestructiveAddUsage(1);
+
+ IR::AbstractSyntaxNode demote_if_node{};
+ demote_if_node.type = Type::If;
+ demote_if_node.data.if_node.cond = cond;
+ demote_if_node.data.if_node.body = demote_blocks[0];
+ demote_if_node.data.if_node.merge = return_block_it->data.block;
+
+ IR::AbstractSyntaxNode demote_node{};
+ demote_node.type = Type::Block;
+ demote_node.data.block = demote_blocks[0];
+
+ IR::AbstractSyntaxNode demote_endif_node{};
+ demote_endif_node.type = Type::EndIf;
+ demote_endif_node.data.end_if.merge = return_block_it->data.block;
+
+ asl.insert(return_block_it, demote_endif_node);
+ asl.insert(return_block_it, demote_node);
+ asl.insert(return_block_it, demote_if_node);
+ }
+
ObjectPool<Statement>& stmt_pool;
ObjectPool<IR::Inst>& inst_pool;
ObjectPool<IR::Block>& block_pool;
Environment& env;
IR::AbstractSyntaxList& syntax_list;
+ bool uses_demote_to_helper{};
// TODO: C++20 Remove this when all compilers support constexpr std::vector
#if __cpp_lib_constexpr_vector >= 201907
@@ -871,12 +989,13 @@ private:
} // Anonymous namespace
IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
- Environment& env, Flow::CFG& cfg) {
+ Environment& env, Flow::CFG& cfg,
+ const HostTranslateInfo& host_info) {
ObjectPool<Statement> stmt_pool{64};
GotoPass goto_pass{cfg, stmt_pool};
Statement& root{goto_pass.RootStatement()};
IR::AbstractSyntaxList syntax_list;
- TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list};
+ TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list, host_info};
return syntax_list;
}
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.h b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h
index 88b083649..e38158da3 100644
--- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.h
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h
@@ -11,10 +11,13 @@
#include "shader_recompiler/frontend/maxwell/control_flow.h"
#include "shader_recompiler/object_pool.h"
-namespace Shader::Maxwell {
+namespace Shader {
+struct HostTranslateInfo;
+namespace Maxwell {
[[nodiscard]] IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool,
ObjectPool<IR::Block>& block_pool, Environment& env,
- Flow::CFG& cfg);
+ Flow::CFG& cfg, const HostTranslateInfo& host_info);
-} // namespace Shader::Maxwell
+} // namespace Maxwell
+} // namespace Shader
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index c067d459c..012d55357 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -130,7 +130,7 @@ void AddNVNStorageBuffers(IR::Program& program) {
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
IR::Program program;
- program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg);
+ program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, host_info);
program.blocks = GenerateBlocks(program.syntax_list);
program.post_order_blocks = PostOrder(program.syntax_list.front());
program.stage = env.ShaderStage();
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index 94a584219..96468b2e7 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -11,8 +11,9 @@ namespace Shader {
/// Misc information about the host
struct HostTranslateInfo {
- bool support_float16{}; ///< True when the device supports 16-bit floats
- bool support_int64{}; ///< True when the device supports 64-bit integers
+ bool support_float16{}; ///< True when the device supports 16-bit floats
+ bool support_int64{}; ///< True when the device supports 64-bit integers
+ bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
};
} // namespace Shader
diff --git a/src/tests/common/param_package.cpp b/src/tests/common/param_package.cpp
index 4c0f9654f..e31ca3544 100644
--- a/src/tests/common/param_package.cpp
+++ b/src/tests/common/param_package.cpp
@@ -4,11 +4,13 @@
#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"},
diff --git a/src/video_core/buffer_cache/buffer_base.h b/src/video_core/buffer_cache/buffer_base.h
index c3318095c..be2113f5a 100644
--- a/src/video_core/buffer_cache/buffer_base.h
+++ b/src/video_core/buffer_cache/buffer_base.h
@@ -261,16 +261,6 @@ public:
stream_score += score;
}
- /// Sets the new frame tick
- void SetFrameTick(u64 new_frame_tick) noexcept {
- frame_tick = new_frame_tick;
- }
-
- /// Returns the new frame tick
- [[nodiscard]] u64 FrameTick() const noexcept {
- return frame_tick;
- }
-
/// Returns the likeliness of this being a stream buffer
[[nodiscard]] int StreamScore() const noexcept {
return stream_score;
@@ -307,6 +297,14 @@ public:
return words.size_bytes;
}
+ size_t getLRUID() const noexcept {
+ return lru_id;
+ }
+
+ void setLRUID(size_t lru_id_) {
+ lru_id = lru_id_;
+ }
+
private:
template <Type type>
u64* Array() noexcept {
@@ -603,9 +601,9 @@ private:
RasterizerInterface* rasterizer = nullptr;
VAddr cpu_addr = 0;
Words words;
- u64 frame_tick = 0;
BufferFlagBits flags{};
int stream_score = 0;
+ size_t lru_id = SIZE_MAX;
};
} // namespace VideoCommon
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index 3b43554f9..7bfd57369 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -20,6 +20,7 @@
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/literals.h"
+#include "common/lru_cache.h"
#include "common/microprofile.h"
#include "common/scope_exit.h"
#include "common/settings.h"
@@ -330,7 +331,7 @@ private:
template <bool insert>
void ChangeRegister(BufferId buffer_id);
- void TouchBuffer(Buffer& buffer) const noexcept;
+ void TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept;
bool SynchronizeBuffer(Buffer& buffer, VAddr cpu_addr, u32 size);
@@ -428,7 +429,11 @@ private:
size_t immediate_buffer_capacity = 0;
std::unique_ptr<u8[]> immediate_buffer_alloc;
- typename SlotVector<Buffer>::Iterator deletion_iterator;
+ struct LRUItemParams {
+ using ObjectType = BufferId;
+ using TickType = u64;
+ };
+ Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
u64 frame_tick = 0;
u64 total_used_memory = 0;
@@ -445,7 +450,6 @@ BufferCache<P>::BufferCache(VideoCore::RasterizerInterface& rasterizer_,
kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_} {
// Ensure the first slot is used for the null buffer
void(slot_buffers.insert(runtime, NullBufferParams{}));
- deletion_iterator = slot_buffers.end();
common_ranges.clear();
}
@@ -454,20 +458,17 @@ void BufferCache<P>::RunGarbageCollector() {
const bool aggressive_gc = total_used_memory >= CRITICAL_MEMORY;
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
int num_iterations = aggressive_gc ? 64 : 32;
- for (; num_iterations > 0; --num_iterations) {
- if (deletion_iterator == slot_buffers.end()) {
- deletion_iterator = slot_buffers.begin();
- }
- ++deletion_iterator;
- if (deletion_iterator == slot_buffers.end()) {
- break;
- }
- const auto [buffer_id, buffer] = *deletion_iterator;
- if (buffer->FrameTick() + ticks_to_destroy < frame_tick) {
- DownloadBufferMemory(*buffer);
- DeleteBuffer(buffer_id);
+ const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
+ if (num_iterations == 0) {
+ return true;
}
- }
+ --num_iterations;
+ auto& buffer = slot_buffers[buffer_id];
+ DownloadBufferMemory(buffer);
+ DeleteBuffer(buffer_id);
+ return false;
+ };
+ lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
}
template <class P>
@@ -485,7 +486,7 @@ void BufferCache<P>::TickFrame() {
const bool skip_preferred = hits * 256 < shots * 251;
uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
- if (Settings::values.use_caches_gc.GetValue() && total_used_memory >= EXPECTED_MEMORY) {
+ if (total_used_memory >= EXPECTED_MEMORY) {
RunGarbageCollector();
}
++frame_tick;
@@ -954,7 +955,7 @@ bool BufferCache<P>::IsRegionCpuModified(VAddr addr, size_t size) {
template <class P>
void BufferCache<P>::BindHostIndexBuffer() {
Buffer& buffer = slot_buffers[index_buffer.buffer_id];
- TouchBuffer(buffer);
+ TouchBuffer(buffer, index_buffer.buffer_id);
const u32 offset = buffer.Offset(index_buffer.cpu_addr);
const u32 size = index_buffer.size;
SynchronizeBuffer(buffer, index_buffer.cpu_addr, size);
@@ -975,7 +976,7 @@ void BufferCache<P>::BindHostVertexBuffers() {
for (u32 index = 0; index < NUM_VERTEX_BUFFERS; ++index) {
const Binding& binding = vertex_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
- TouchBuffer(buffer);
+ TouchBuffer(buffer, binding.buffer_id);
SynchronizeBuffer(buffer, binding.cpu_addr, binding.size);
if (!flags[Dirty::VertexBuffer0 + index]) {
continue;
@@ -1011,7 +1012,7 @@ void BufferCache<P>::BindHostGraphicsUniformBuffer(size_t stage, u32 index, u32
const VAddr cpu_addr = binding.cpu_addr;
const u32 size = std::min(binding.size, (*uniform_buffer_sizes)[stage][index]);
Buffer& buffer = slot_buffers[binding.buffer_id];
- TouchBuffer(buffer);
+ TouchBuffer(buffer, binding.buffer_id);
const bool use_fast_buffer = binding.buffer_id != NULL_BUFFER_ID &&
size <= uniform_buffer_skip_cache_size &&
!buffer.IsRegionGpuModified(cpu_addr, size);
@@ -1083,7 +1084,7 @@ void BufferCache<P>::BindHostGraphicsStorageBuffers(size_t stage) {
ForEachEnabledBit(enabled_storage_buffers[stage], [&](u32 index) {
const Binding& binding = storage_buffers[stage][index];
Buffer& buffer = slot_buffers[binding.buffer_id];
- TouchBuffer(buffer);
+ TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
@@ -1128,7 +1129,7 @@ void BufferCache<P>::BindHostTransformFeedbackBuffers() {
for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) {
const Binding& binding = transform_feedback_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
- TouchBuffer(buffer);
+ TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
@@ -1148,7 +1149,7 @@ void BufferCache<P>::BindHostComputeUniformBuffers() {
ForEachEnabledBit(enabled_compute_uniform_buffer_mask, [&](u32 index) {
const Binding& binding = compute_uniform_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
- TouchBuffer(buffer);
+ TouchBuffer(buffer, binding.buffer_id);
const u32 size = std::min(binding.size, (*compute_uniform_buffer_sizes)[index]);
SynchronizeBuffer(buffer, binding.cpu_addr, size);
@@ -1168,7 +1169,7 @@ void BufferCache<P>::BindHostComputeStorageBuffers() {
ForEachEnabledBit(enabled_compute_storage_buffers, [&](u32 index) {
const Binding& binding = compute_storage_buffers[index];
Buffer& buffer = slot_buffers[binding.buffer_id];
- TouchBuffer(buffer);
+ TouchBuffer(buffer, binding.buffer_id);
const u32 size = binding.size;
SynchronizeBuffer(buffer, binding.cpu_addr, size);
@@ -1513,11 +1514,11 @@ BufferId BufferCache<P>::CreateBuffer(VAddr cpu_addr, u32 wanted_size) {
const OverlapResult overlap = ResolveOverlaps(cpu_addr, wanted_size);
const u32 size = static_cast<u32>(overlap.end - overlap.begin);
const BufferId new_buffer_id = slot_buffers.insert(runtime, rasterizer, overlap.begin, size);
- TouchBuffer(slot_buffers[new_buffer_id]);
for (const BufferId overlap_id : overlap.ids) {
JoinOverlap(new_buffer_id, overlap_id, !overlap.has_stream_leap);
}
Register(new_buffer_id);
+ TouchBuffer(slot_buffers[new_buffer_id], new_buffer_id);
return new_buffer_id;
}
@@ -1534,12 +1535,14 @@ void BufferCache<P>::Unregister(BufferId buffer_id) {
template <class P>
template <bool insert>
void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
- const Buffer& buffer = slot_buffers[buffer_id];
+ Buffer& buffer = slot_buffers[buffer_id];
const auto size = buffer.SizeBytes();
if (insert) {
total_used_memory += Common::AlignUp(size, 1024);
+ buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick));
} else {
total_used_memory -= Common::AlignUp(size, 1024);
+ lru_cache.Free(buffer.getLRUID());
}
const VAddr cpu_addr_begin = buffer.CpuAddr();
const VAddr cpu_addr_end = cpu_addr_begin + size;
@@ -1555,8 +1558,10 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
}
template <class P>
-void BufferCache<P>::TouchBuffer(Buffer& buffer) const noexcept {
- buffer.SetFrameTick(frame_tick);
+void BufferCache<P>::TouchBuffer(Buffer& buffer, BufferId buffer_id) noexcept {
+ if (buffer_id != NULL_BUFFER_ID) {
+ lru_cache.Touch(buffer.getLRUID(), frame_tick);
+ }
}
template <class P>
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
index 70030066a..d7e749485 100644
--- a/src/video_core/command_classes/codecs/vp9.cpp
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -742,6 +742,7 @@ VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q);
uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q);
+ ASSERT(!current_frame_info.segment_enabled);
uncomp_writer.WriteBit(false); // Segmentation enabled (TODO).
const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width);
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
index 87eafdb03..3b1ed4b3a 100644
--- a/src/video_core/command_classes/codecs/vp9_types.h
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -22,7 +22,7 @@ struct Vp9FrameDimensions {
};
static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
-enum FrameFlags : u32 {
+enum class FrameFlags : u32 {
IsKeyFrame = 1 << 0,
LastFrameIsKeyFrame = 1 << 1,
FrameSizeChanged = 1 << 2,
@@ -30,6 +30,7 @@ enum FrameFlags : u32 {
LastShowFrame = 1 << 4,
IntraOnly = 1 << 5,
};
+DECLARE_ENUM_FLAG_OPERATORS(FrameFlags)
enum class TxSize {
Tx4x4 = 0, // 4x4 transform
@@ -92,44 +93,34 @@ struct Vp9EntropyProbs {
static_assert(sizeof(Vp9EntropyProbs) == 0x7B4, "Vp9EntropyProbs is an invalid size");
struct Vp9PictureInfo {
- bool is_key_frame;
- bool intra_only;
- bool last_frame_was_key;
- bool frame_size_changed;
- bool error_resilient_mode;
- bool last_frame_shown;
- bool show_frame;
+ u32 bitstream_size;
+ std::array<u64, 4> frame_offsets;
std::array<s8, 4> ref_frame_sign_bias;
s32 base_q_index;
s32 y_dc_delta_q;
s32 uv_dc_delta_q;
s32 uv_ac_delta_q;
- bool lossless;
s32 transform_mode;
- bool allow_high_precision_mv;
s32 interp_filter;
s32 reference_mode;
- s8 comp_fixed_ref;
- std::array<s8, 2> comp_var_ref;
s32 log2_tile_cols;
s32 log2_tile_rows;
- bool segment_enabled;
- bool segment_map_update;
- bool segment_map_temporal_update;
- s32 segment_abs_delta;
- std::array<u32, 8> segment_feature_enable;
- std::array<std::array<s16, 4>, 8> segment_feature_data;
- bool mode_ref_delta_enabled;
- bool use_prev_in_find_mv_refs;
std::array<s8, 4> ref_deltas;
std::array<s8, 2> mode_deltas;
Vp9EntropyProbs entropy;
Vp9FrameDimensions frame_size;
u8 first_level;
u8 sharpness_level;
- u32 bitstream_size;
- std::array<u64, 4> frame_offsets;
- std::array<bool, 4> refresh_frame;
+ bool is_key_frame;
+ bool intra_only;
+ bool last_frame_was_key;
+ bool error_resilient_mode;
+ bool last_frame_shown;
+ bool show_frame;
+ bool lossless;
+ bool allow_high_precision_mv;
+ bool segment_enabled;
+ bool mode_ref_delta_enabled;
};
struct Vp9FrameContainer {
@@ -145,7 +136,7 @@ struct PictureInfo {
Vp9FrameDimensions golden_frame_size; ///< 0x50
Vp9FrameDimensions alt_frame_size; ///< 0x58
Vp9FrameDimensions current_frame_size; ///< 0x60
- u32 vp9_flags; ///< 0x68
+ FrameFlags vp9_flags; ///< 0x68
std::array<s8, 4> ref_frame_sign_bias; ///< 0x6C
u8 first_level; ///< 0x70
u8 sharpness_level; ///< 0x71
@@ -158,60 +149,43 @@ struct PictureInfo {
u8 allow_high_precision_mv; ///< 0x78
u8 interp_filter; ///< 0x79
u8 reference_mode; ///< 0x7A
- s8 comp_fixed_ref; ///< 0x7B
- std::array<s8, 2> comp_var_ref; ///< 0x7C
+ INSERT_PADDING_BYTES_NOINIT(3); ///< 0x7B
u8 log2_tile_cols; ///< 0x7E
u8 log2_tile_rows; ///< 0x7F
Segmentation segmentation; ///< 0x80
LoopFilter loop_filter; ///< 0xE4
- INSERT_PADDING_BYTES_NOINIT(5); ///< 0xEB
- u32 surface_params; ///< 0xF0
- INSERT_PADDING_WORDS_NOINIT(3); ///< 0xF4
+ INSERT_PADDING_BYTES_NOINIT(21); ///< 0xEB
[[nodiscard]] Vp9PictureInfo Convert() const {
return {
- .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0,
- .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0,
- .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0,
- .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
- .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
- .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
- .show_frame = true,
+ .bitstream_size = bitstream_size,
+ .frame_offsets{},
.ref_frame_sign_bias = ref_frame_sign_bias,
.base_q_index = base_q_index,
.y_dc_delta_q = y_dc_delta_q,
.uv_dc_delta_q = uv_dc_delta_q,
.uv_ac_delta_q = uv_ac_delta_q,
- .lossless = lossless != 0,
.transform_mode = tx_mode,
- .allow_high_precision_mv = allow_high_precision_mv != 0,
.interp_filter = interp_filter,
.reference_mode = reference_mode,
- .comp_fixed_ref = comp_fixed_ref,
- .comp_var_ref = comp_var_ref,
.log2_tile_cols = log2_tile_cols,
.log2_tile_rows = log2_tile_rows,
- .segment_enabled = segmentation.enabled != 0,
- .segment_map_update = segmentation.update_map != 0,
- .segment_map_temporal_update = segmentation.temporal_update != 0,
- .segment_abs_delta = segmentation.abs_delta,
- .segment_feature_enable = segmentation.feature_mask,
- .segment_feature_data = segmentation.feature_data,
- .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
- .use_prev_in_find_mv_refs = !(vp9_flags == (FrameFlags::ErrorResilientMode)) &&
- !(vp9_flags == (FrameFlags::FrameSizeChanged)) &&
- !(vp9_flags == (FrameFlags::IntraOnly)) &&
- (vp9_flags == (FrameFlags::LastShowFrame)) &&
- !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)),
.ref_deltas = loop_filter.ref_deltas,
.mode_deltas = loop_filter.mode_deltas,
.entropy{},
.frame_size = current_frame_size,
.first_level = first_level,
.sharpness_level = sharpness_level,
- .bitstream_size = bitstream_size,
- .frame_offsets{},
- .refresh_frame{},
+ .is_key_frame = True(vp9_flags & FrameFlags::IsKeyFrame),
+ .intra_only = True(vp9_flags & FrameFlags::IntraOnly),
+ .last_frame_was_key = True(vp9_flags & FrameFlags::LastFrameIsKeyFrame),
+ .error_resilient_mode = True(vp9_flags & FrameFlags::ErrorResilientMode),
+ .last_frame_shown = True(vp9_flags & FrameFlags::LastShowFrame),
+ .show_frame = true,
+ .lossless = lossless != 0,
+ .allow_high_precision_mv = allow_high_precision_mv != 0,
+ .segment_enabled = segmentation.enabled != 0,
+ .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
};
}
};
@@ -316,7 +290,6 @@ ASSERT_POSITION(last_frame_size, 0x48);
ASSERT_POSITION(first_level, 0x70);
ASSERT_POSITION(segmentation, 0x80);
ASSERT_POSITION(loop_filter, 0xE4);
-ASSERT_POSITION(surface_params, 0xF0);
#undef ASSERT_POSITION
#define ASSERT_POSITION(field_name, position) \
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
index d5e77941c..0ee07f398 100644
--- a/src/video_core/command_classes/vic.cpp
+++ b/src/video_core/command_classes/vic.cpp
@@ -96,12 +96,11 @@ void Vic::Execute() {
if (!converted_frame_buffer) {
converted_frame_buffer = AVMallocPtr{static_cast<u8*>(av_malloc(linear_size)), av_free};
}
-
- const int converted_stride{frame->width * 4};
+ const std::array<int, 4> converted_stride{frame->width * 4, frame->height * 4, 0, 0};
u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height,
- &converted_frame_buf_addr, &converted_stride);
+ &converted_frame_buf_addr, converted_stride.data());
const u32 blk_kind = static_cast<u32>(config.block_linear_kind);
if (blk_kind != 0) {
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 1aa43523a..7f4ca6282 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -475,10 +475,10 @@ public:
// These values are used by Nouveau and some games.
AddGL = 0x8006,
- SubtractGL = 0x8007,
- ReverseSubtractGL = 0x8008,
- MinGL = 0x800a,
- MaxGL = 0x800b
+ MinGL = 0x8007,
+ MaxGL = 0x8008,
+ SubtractGL = 0x800a,
+ ReverseSubtractGL = 0x800b
};
enum class Factor : u32 {
diff --git a/src/video_core/macro/macro_jit_x64.h b/src/video_core/macro/macro_jit_x64.h
index 7f50ac2f8..d03d480b4 100644
--- a/src/video_core/macro/macro_jit_x64.h
+++ b/src/video_core/macro/macro_jit_x64.h
@@ -6,7 +6,7 @@
#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"
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index 882eff880..c60ed6453 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -463,6 +463,7 @@ std::vector<std::pair<GPUVAddr, std::size_t>> MemoryManager::GetSubmappedRange(
++page_index;
page_offset = 0;
remaining_size -= num_bytes;
+ old_page_addr = page_addr;
}
split();
return result;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index ee992aed4..de9e41659 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -156,6 +156,10 @@ public:
return shader_backend;
}
+ bool IsAmd() const {
+ return vendor_name == "ATI Technologies Inc.";
+ }
+
private:
static bool TestVariableAoffi();
static bool TestPreciseBug();
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 1f4dda17e..b0e14182e 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -219,6 +219,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
host_info{
.support_float16 = false,
.support_int64 = device.HasShaderInt64(),
+ .needs_demote_reorder = device.IsAmd(),
} {
if (use_asynchronous_shaders) {
workers = CreateWorkers();
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 7c9b0d6db..9ff0a28cd 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -164,7 +164,8 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
blit_screen.Recreate();
}
const VkSemaphore render_semaphore = blit_screen.DrawToSwapchain(*framebuffer, use_accelerated);
- scheduler.Flush(render_semaphore);
+ const VkSemaphore present_semaphore = swapchain.CurrentPresentSemaphore();
+ scheduler.Flush(render_semaphore, present_semaphore);
scheduler.WaitWorker();
swapchain.Present(render_semaphore);
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index 5c43b8acf..888bc7392 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -159,11 +159,13 @@ VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
const u8* const host_ptr = cpu_memory.GetPointer(framebuffer_addr);
- const size_t size_bytes = GetSizeInBytes(framebuffer);
// TODO(Rodrigo): Read this from HLE
constexpr u32 block_height_log2 = 4;
const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer);
+ const u64 size_bytes{Tegra::Texture::CalculateSize(true, bytes_per_pixel,
+ framebuffer.stride, framebuffer.height,
+ 1, block_height_log2, 0)};
Tegra::Texture::UnswizzleTexture(
mapped_span.subspan(image_offset, size_bytes), std::span(host_ptr, size_bytes),
bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0);
@@ -356,7 +358,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,
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index a37ca1fdf..31bfbcb06 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -281,7 +281,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw
.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,
@@ -325,6 +325,8 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw
host_info = Shader::HostTranslateInfo{
.support_float16 = device.IsFloat16Supported(),
.support_int64 = device.IsShaderInt64Supported(),
+ .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR ||
+ driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR,
};
}
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 3ac18ea54..841a6b846 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -228,9 +228,7 @@ void RasterizerVulkan::Clear() {
};
const u32 color_attachment = regs.clear_buffers.RT;
- const auto attachment_aspect_mask = framebuffer->ImageRanges()[color_attachment].aspectMask;
- const bool is_color_rt = (attachment_aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
- if (use_color && is_color_rt) {
+ if (use_color && framebuffer->HasAspectColorBit(color_attachment)) {
VkClearValue clear_value;
std::memcpy(clear_value.color.float32, regs.clear_color, sizeof(regs.clear_color));
@@ -248,12 +246,15 @@ void RasterizerVulkan::Clear() {
return;
}
VkImageAspectFlags aspect_flags = 0;
- if (use_depth) {
+ if (use_depth && framebuffer->HasAspectDepthBit()) {
aspect_flags |= VK_IMAGE_ASPECT_DEPTH_BIT;
}
- if (use_stencil) {
+ if (use_stencil && framebuffer->HasAspectStencilBit()) {
aspect_flags |= VK_IMAGE_ASPECT_STENCIL_BIT;
}
+ if (aspect_flags == 0) {
+ return;
+ }
scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
clear_rect, aspect_flags](vk::CommandBuffer cmdbuf) {
VkClearAttachment attachment;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 4840962de..1d438787a 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -55,14 +55,14 @@ VKScheduler::~VKScheduler() {
worker_thread.join();
}
-void VKScheduler::Flush(VkSemaphore semaphore) {
- SubmitExecution(semaphore);
+void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
+ SubmitExecution(signal_semaphore, wait_semaphore);
AllocateNewContext();
}
-void VKScheduler::Finish(VkSemaphore semaphore) {
+void VKScheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
const u64 presubmit_tick = CurrentTick();
- SubmitExecution(semaphore);
+ SubmitExecution(signal_semaphore, wait_semaphore);
WaitWorker();
Wait(presubmit_tick);
AllocateNewContext();
@@ -171,37 +171,41 @@ void VKScheduler::AllocateWorkerCommandBuffer() {
});
}
-void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
+void VKScheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
EndPendingOperations();
InvalidateState();
const u64 signal_value = master_semaphore->NextTick();
- Record([semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
+ Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
cmdbuf.End();
-
- const u32 num_signal_semaphores = semaphore ? 2U : 1U;
-
- const u64 wait_value = signal_value - 1;
- const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
-
const VkSemaphore timeline_semaphore = master_semaphore->Handle();
+
+ const u32 num_signal_semaphores = signal_semaphore ? 2U : 1U;
const std::array signal_values{signal_value, u64(0)};
- const std::array signal_semaphores{timeline_semaphore, semaphore};
+ const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
+
+ const u32 num_wait_semaphores = wait_semaphore ? 2U : 1U;
+ const std::array wait_values{signal_value - 1, u64(1)};
+ const std::array wait_semaphores{timeline_semaphore, wait_semaphore};
+ static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
+ VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
+ };
const VkTimelineSemaphoreSubmitInfoKHR timeline_si{
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
.pNext = nullptr,
- .waitSemaphoreValueCount = 1,
- .pWaitSemaphoreValues = &wait_value,
+ .waitSemaphoreValueCount = num_wait_semaphores,
+ .pWaitSemaphoreValues = wait_values.data(),
.signalSemaphoreValueCount = num_signal_semaphores,
.pSignalSemaphoreValues = signal_values.data(),
};
const VkSubmitInfo submit_info{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = &timeline_si,
- .waitSemaphoreCount = 1,
- .pWaitSemaphores = &timeline_semaphore,
- .pWaitDstStageMask = &wait_stage_mask,
+ .waitSemaphoreCount = num_wait_semaphores,
+ .pWaitSemaphores = wait_semaphores.data(),
+ .pWaitDstStageMask = wait_stage_masks.data(),
.commandBufferCount = 1,
.pCommandBuffers = cmdbuf.address(),
.signalSemaphoreCount = num_signal_semaphores,
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index cf39a2363..759ed5a48 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -34,10 +34,10 @@ public:
~VKScheduler();
/// Sends the current execution context to the GPU.
- void Flush(VkSemaphore semaphore = nullptr);
+ void Flush(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
/// Sends the current execution context to the GPU and waits for it to complete.
- void Finish(VkSemaphore semaphore = nullptr);
+ void Finish(VkSemaphore signal_semaphore = nullptr, VkSemaphore wait_semaphore = nullptr);
/// Waits for the worker thread to finish executing everything. After this function returns it's
/// safe to touch worker resources.
@@ -191,7 +191,7 @@ private:
void AllocateWorkerCommandBuffer();
- void SubmitExecution(VkSemaphore semaphore);
+ void SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore);
void AllocateNewContext();
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index d990eefba..aadf03cb0 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -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];
@@ -107,14 +106,12 @@ void VKSwapchain::AcquireNextImage() {
}
void VKSwapchain::Present(VkSemaphore render_semaphore) {
- const VkSemaphore present_semaphore{*present_semaphores[frame_index]};
- const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore};
const auto present_queue{device.GetPresentQueue()};
const VkPresentInfoKHR present_info{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.pNext = nullptr,
- .waitSemaphoreCount = render_semaphore ? 2U : 1U,
- .pWaitSemaphores = semaphores.data(),
+ .waitSemaphoreCount = render_semaphore ? 1U : 0U,
+ .pWaitSemaphores = &render_semaphore,
.swapchainCount = 1,
.pSwapchains = swapchain.address(),
.pImageIndices = &image_index,
@@ -145,7 +142,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 +177,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 +199,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 +215,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,
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index 35c2cdc14..5bce41e21 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -68,8 +68,12 @@ public:
return *image_views[index];
}
- VkFormat GetImageFormat() const {
- return image_format;
+ VkFormat GetImageViewFormat() const {
+ return image_view_format;
+ }
+
+ VkSemaphore CurrentPresentSemaphore() const {
+ return *present_semaphores[frame_index];
}
private:
@@ -96,7 +100,7 @@ private:
u32 image_index{};
u32 frame_index{};
- VkFormat image_format{};
+ VkFormat image_view_format{};
VkExtent2D extent{};
bool current_srgb{};
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 8f4df7122..ff979a7ac 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1186,9 +1186,12 @@ Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM
renderpass_key.depth_format = depth_buffer->format;
num_layers = std::max(num_layers, depth_buffer->range.extent.layers);
images[num_images] = depth_buffer->ImageHandle();
- image_ranges[num_images] = MakeSubresourceRange(depth_buffer);
+ const VkImageSubresourceRange subresource_range = MakeSubresourceRange(depth_buffer);
+ image_ranges[num_images] = subresource_range;
samples = depth_buffer->Samples();
++num_images;
+ has_depth = (subresource_range.aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
+ has_stencil = (subresource_range.aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
} else {
renderpass_key.depth_format = PixelFormat::Invalid;
}
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 5fe6b7ba3..6d5a68bfe 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -232,6 +232,18 @@ public:
return image_ranges;
}
+ [[nodiscard]] bool HasAspectColorBit(size_t index) const noexcept {
+ return (image_ranges.at(index).aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;
+ }
+
+ [[nodiscard]] bool HasAspectDepthBit() const noexcept {
+ return has_depth;
+ }
+
+ [[nodiscard]] bool HasAspectStencilBit() const noexcept {
+ return has_stencil;
+ }
+
private:
vk::Framebuffer framebuffer;
VkRenderPass renderpass{};
@@ -241,6 +253,8 @@ private:
u32 num_images = 0;
std::array<VkImage, 9> images{};
std::array<VkImageSubresourceRange, 9> image_ranges{};
+ bool has_depth{};
+ bool has_stencil{};
};
struct TextureCacheParams {
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index ff1feda9b..0c17a791b 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -80,7 +80,7 @@ struct ImageBase {
VAddr cpu_addr_end = 0;
u64 modification_tick = 0;
- u64 frame_tick = 0;
+ size_t lru_index = SIZE_MAX;
std::array<u32, MAX_MIP_LEVELS> mip_level_offsets{};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index a087498ff..24b809242 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -5,7 +5,6 @@
#pragma once
#include "common/alignment.h"
-#include "common/settings.h"
#include "video_core/dirty_flags.h"
#include "video_core/texture_cache/samples_helper.h"
#include "video_core/texture_cache/texture_cache_base.h"
@@ -43,8 +42,6 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
void(slot_image_views.insert(runtime, NullImageParams{}));
void(slot_samplers.insert(runtime, sampler_descriptor));
- deletion_iterator = slot_images.begin();
-
if constexpr (HAS_DEVICE_MEMORY_INFO) {
const auto device_memory = runtime.GetDeviceLocalMemory();
const u64 possible_expected_memory = (device_memory * 3) / 10;
@@ -64,70 +61,38 @@ template <class P>
void TextureCache<P>::RunGarbageCollector() {
const bool high_priority_mode = total_used_memory >= expected_memory;
const bool aggressive_mode = total_used_memory >= critical_memory;
- const u64 ticks_to_destroy = high_priority_mode ? 60 : 100;
- int num_iterations = aggressive_mode ? 256 : (high_priority_mode ? 128 : 64);
- for (; num_iterations > 0; --num_iterations) {
- if (deletion_iterator == slot_images.end()) {
- deletion_iterator = slot_images.begin();
- if (deletion_iterator == slot_images.end()) {
- break;
- }
+ const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 100ULL;
+ size_t num_iterations = aggressive_mode ? 10000 : (high_priority_mode ? 100 : 5);
+ const auto clean_up = [this, &num_iterations, high_priority_mode](ImageId image_id) {
+ if (num_iterations == 0) {
+ return true;
}
- auto [image_id, image_tmp] = *deletion_iterator;
- Image* image = image_tmp; // fix clang error.
- const bool is_alias = True(image->flags & ImageFlagBits::Alias);
- const bool is_bad_overlap = True(image->flags & ImageFlagBits::BadOverlap);
- const bool must_download = image->IsSafeDownload();
- bool should_care = is_bad_overlap || is_alias || (high_priority_mode && !must_download);
- const u64 ticks_needed =
- is_bad_overlap
- ? ticks_to_destroy >> 4
- : ((should_care && aggressive_mode) ? ticks_to_destroy >> 1 : ticks_to_destroy);
- should_care |= aggressive_mode;
- if (should_care && image->frame_tick + ticks_needed < frame_tick) {
- if (is_bad_overlap) {
- const bool overlap_check = std::ranges::all_of(
- image->overlapping_images, [&, image](const ImageId& overlap_id) {
- auto& overlap = slot_images[overlap_id];
- return overlap.frame_tick >= image->frame_tick;
- });
- if (!overlap_check) {
- ++deletion_iterator;
- continue;
- }
- }
- if (!is_bad_overlap && must_download) {
- const bool alias_check = std::ranges::none_of(
- image->aliased_images, [&, image](const AliasedImage& alias) {
- auto& alias_image = slot_images[alias.id];
- return (alias_image.frame_tick < image->frame_tick) ||
- (alias_image.modification_tick < image->modification_tick);
- });
-
- if (alias_check) {
- auto map = runtime.DownloadStagingBuffer(image->unswizzled_size_bytes);
- const auto copies = FullDownloadCopies(image->info);
- image->DownloadMemory(map, copies);
- runtime.Finish();
- SwizzleImage(gpu_memory, image->gpu_addr, image->info, copies, map.mapped_span);
- }
- }
- if (True(image->flags & ImageFlagBits::Tracked)) {
- UntrackImage(*image, image_id);
- }
- UnregisterImage(image_id);
- DeleteImage(image_id);
- if (is_bad_overlap) {
- ++num_iterations;
- }
+ --num_iterations;
+ auto& image = slot_images[image_id];
+ const bool must_download = image.IsSafeDownload();
+ if (!high_priority_mode && must_download) {
+ return false;
}
- ++deletion_iterator;
- }
+ if (must_download) {
+ auto map = runtime.DownloadStagingBuffer(image.unswizzled_size_bytes);
+ const auto copies = FullDownloadCopies(image.info);
+ image.DownloadMemory(map, copies);
+ runtime.Finish();
+ SwizzleImage(gpu_memory, image.gpu_addr, image.info, copies, map.mapped_span);
+ }
+ if (True(image.flags & ImageFlagBits::Tracked)) {
+ UntrackImage(image, image_id);
+ }
+ UnregisterImage(image_id);
+ DeleteImage(image_id);
+ return false;
+ };
+ lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
}
template <class P>
void TextureCache<P>::TickFrame() {
- if (Settings::values.use_caches_gc.GetValue() && total_used_memory > minimum_memory) {
+ if (total_used_memory > minimum_memory) {
RunGarbageCollector();
}
sentenced_images.Tick();
@@ -1078,6 +1043,8 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
}
total_used_memory += Common::AlignUp(tentative_size, 1024);
+ image.lru_index = lru_cache.Insert(image_id, frame_tick);
+
ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
[this, image_id](u64 page) { gpu_page_table[page].push_back(image_id); });
if (False(image.flags & ImageFlagBits::Sparse)) {
@@ -1115,6 +1082,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
tentative_size = EstimatedDecompressedSize(tentative_size, image.info.format);
}
total_used_memory -= Common::AlignUp(tentative_size, 1024);
+ lru_cache.Free(image.lru_index);
const auto& clear_page_table =
[this, image_id](
u64 page,
@@ -1384,7 +1352,7 @@ void TextureCache<P>::PrepareImage(ImageId image_id, bool is_modification, bool
if (is_modification) {
MarkModification(image);
}
- image.frame_tick = frame_tick;
+ lru_cache.Touch(image.lru_index, frame_tick);
}
template <class P>
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index e4ae351cb..d7528ed24 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -14,6 +14,7 @@
#include "common/common_types.h"
#include "common/literals.h"
+#include "common/lru_cache.h"
#include "video_core/compatible_formats.h"
#include "video_core/delayed_destruction_ring.h"
#include "video_core/engines/fermi_2d.h"
@@ -370,6 +371,12 @@ private:
std::vector<ImageId> uncommitted_downloads;
std::queue<std::vector<ImageId>> committed_downloads;
+ struct LRUItemParams {
+ using ObjectType = ImageId;
+ using TickType = u64;
+ };
+ Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
+
static constexpr size_t TICKS_TO_DESTROY = 6;
DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images;
DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view;
@@ -379,7 +386,6 @@ private:
u64 modification_tick = 0;
u64 frame_tick = 0;
- typename SlotVector<Image>::Iterator deletion_iterator;
};
} // namespace VideoCommon
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index c32ae956a..24e943e4c 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -63,14 +63,6 @@ void SwizzleImpl(std::span<u8> output, std::span<const u8> input, u32 width, u32
const u32 unswizzled_offset =
slice * pitch * height + line * pitch + column * BYTES_PER_PIXEL;
- if (const auto offset = (TO_LINEAR ? unswizzled_offset : swizzled_offset);
- offset >= input.size()) {
- // TODO(Rodrigo): This is an out of bounds access that should never happen. To
- // avoid crashing the emulator, break.
- ASSERT_MSG(false, "offset {} exceeds input size {}!", offset, input.size());
- break;
- }
-
u8* const dst = &output[TO_LINEAR ? swizzled_offset : unswizzled_offset];
const u8* const src = &input[TO_LINEAR ? unswizzled_offset : swizzled_offset];
@@ -84,56 +76,31 @@ 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,
- 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,
+#define BPP_CASE(x) \
+ case x: \
+ return SwizzleImpl<TO_LINEAR, x>(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);
}
}
-} // Anonymous namespace
-
-void UnswizzleTexture(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) {
- Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
- stride_alignment);
-}
-
-void SwizzleTexture(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) {
- Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
- stride_alignment);
-}
+template <u32 BYTES_PER_PIXEL>
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) {
+ 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;
+ (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 =
@@ -143,20 +110,21 @@ void SwizzleSubrect(u32 subrect_width, u32 subrect_height, u32 source_pitch, u32
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;
+ 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);
+ std::memcpy(dest_addr, source_line, 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;
+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);
@@ -171,24 +139,25 @@ void UnswizzleSubrect(u32 line_length_in, u32 line_count, u32 pitch, u32 width,
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_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;
+ const u32 unswizzled_offset = line * pitch + column * BYTES_PER_PIXEL;
- std::memcpy(output + unswizzled_offset, input + swizzled_offset, 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 bytes_per_pixel, u32 block_height, u32 block_depth, u32 origin_x,
- u32 origin_y, u8* output, const u8* input) {
+ 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 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);
@@ -203,11 +172,93 @@ void SwizzleSliceToVoxel(u32 line_length_in, u32 line_count, u32 pitch, u32 widt
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);
+ 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,
+ u32 width, u32 height, u32 depth, u32 block_height, u32 block_depth,
+ u32 stride_alignment) {
+ Swizzle<false>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
+ stride_alignment);
+}
+
+void SwizzleTexture(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) {
+ Swizzle<true>(output, input, bytes_per_pixel, width, height, depth, block_height, block_depth,
+ stride_alignment);
+}
+
+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) {
+ 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) {
+ 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) {
+ 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);
+ }
+}
void SwizzleKepler(const u32 width, const u32 height, const u32 dst_x, const u32 dst_y,
const u32 block_height_bit, const std::size_t copy_size, const u8* source_data,
@@ -228,7 +279,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;
}
}
}
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index 1a9399455..7994cb859 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -159,7 +159,7 @@ static_assert(sizeof(TextureHandle) == 4, "TextureHandle has wrong size");
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};
}
}
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 8e56a89e1..24821c1a3 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -368,18 +368,21 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
};
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;
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index c19f40746..5599c38c5 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -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.
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index cb4bdcc7e..19ba0dbba 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -102,9 +102,9 @@ add_executable(yuzu
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()
diff --git a/src/yuzu/applets/qt_software_keyboard.cpp b/src/yuzu/applets/qt_software_keyboard.cpp
index 848801cec..8fc0c5a36 100644
--- a/src/yuzu/applets/qt_software_keyboard.cpp
+++ b/src/yuzu/applets/qt_software_keyboard.cpp
@@ -438,7 +438,7 @@ void QtSoftwareKeyboardDialog::ShowInlineKeyboard(
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();
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 6aec3c46c..8744d8e5d 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -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();
}
@@ -817,7 +818,6 @@ void Config::ReadRendererValues() {
ReadGlobalSetting(Settings::values.shader_backend);
ReadGlobalSetting(Settings::values.use_asynchronous_shaders);
ReadGlobalSetting(Settings::values.use_fast_gpu_time);
- ReadGlobalSetting(Settings::values.use_caches_gc);
ReadGlobalSetting(Settings::values.bg_red);
ReadGlobalSetting(Settings::values.bg_green);
ReadGlobalSetting(Settings::values.bg_blue);
@@ -1144,7 +1144,7 @@ void Config::SaveValues() {
SaveDataStorageValues();
SaveDebuggingValues();
SaveDisabledAddOnValues();
- SaveServiceValues();
+ SaveNetworkValues();
SaveUIValues();
SaveWebServiceValues();
SaveMiscellaneousValues();
@@ -1238,11 +1238,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();
}
@@ -1360,7 +1361,6 @@ void Config::SaveRendererValues() {
Settings::values.shader_backend.UsingGlobal());
WriteGlobalSetting(Settings::values.use_asynchronous_shaders);
WriteGlobalSetting(Settings::values.use_fast_gpu_time);
- WriteGlobalSetting(Settings::values.use_caches_gc);
WriteGlobalSetting(Settings::values.bg_red);
WriteGlobalSetting(Settings::values.bg_green);
WriteGlobalSetting(Settings::values.bg_blue);
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 334623997..4733227b6 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -88,7 +88,7 @@ private:
void SaveCoreValues();
void SaveDataStorageValues();
void SaveDebuggingValues();
- void SaveServiceValues();
+ void SaveNetworkValues();
void SaveDisabledAddOnValues();
void SaveMiscellaneousValues();
void SavePathValues();
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index fca9aed5f..6258dcf20 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -147,12 +147,12 @@
<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>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index bc009b6b3..fe4186157 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -67,7 +67,7 @@ void ConfigureDialog::ApplyConfiguration() {
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}},
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 41e930f6b..1a12cfa4d 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -156,7 +156,7 @@
<item>
<widget class="QCheckBox" name="use_disk_shader_cache">
<property name="text">
- <string>Use disk shader cache</string>
+ <string>Use disk pipeline cache</string>
</property>
</widget>
</item>
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index a31b8e192..bfd464061 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -28,7 +28,6 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue());
ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
- ui->use_caches_gc->setChecked(Settings::values.use_caches_gc.GetValue());
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
if (Settings::IsConfiguringGlobal()) {
@@ -55,8 +54,6 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_shaders,
ui->use_asynchronous_shaders,
use_asynchronous_shaders);
- ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_caches_gc, ui->use_caches_gc,
- use_caches_gc);
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time,
ui->use_fast_gpu_time, use_fast_gpu_time);
}
@@ -81,7 +78,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
ui->use_asynchronous_shaders->setEnabled(
Settings::values.use_asynchronous_shaders.UsingGlobal());
ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal());
- ui->use_caches_gc->setEnabled(Settings::values.use_caches_gc.UsingGlobal());
ui->anisotropic_filtering_combobox->setEnabled(
Settings::values.max_anisotropy.UsingGlobal());
@@ -94,8 +90,6 @@ void ConfigureGraphicsAdvanced::SetupPerGameUI() {
use_asynchronous_shaders);
ConfigurationShared::SetColoredTristate(ui->use_fast_gpu_time,
Settings::values.use_fast_gpu_time, use_fast_gpu_time);
- ConfigurationShared::SetColoredTristate(ui->use_caches_gc, Settings::values.use_caches_gc,
- use_caches_gc);
ConfigurationShared::SetColoredComboBox(
ui->gpu_accuracy, ui->label_gpu_accuracy,
static_cast<int>(Settings::values.gpu_accuracy.GetValue(true)));
diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h
index 7356e6916..13ba4ff6b 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.h
+++ b/src/yuzu/configuration/configure_graphics_advanced.h
@@ -37,5 +37,4 @@ private:
ConfigurationShared::CheckState use_vsync;
ConfigurationShared::CheckState use_asynchronous_shaders;
ConfigurationShared::CheckState use_fast_gpu_time;
- ConfigurationShared::CheckState use_caches_gc;
};
diff --git a/src/yuzu/configuration/configure_graphics_advanced.ui b/src/yuzu/configuration/configure_graphics_advanced.ui
index 4fe6b86ae..b91abc2f0 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.ui
+++ b/src/yuzu/configuration/configure_graphics_advanced.ui
@@ -82,7 +82,7 @@
<string>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</string>
</property>
<property name="text">
- <string>Use asynchronous shader building (hack)</string>
+ <string>Use asynchronous shader building (Hack)</string>
</property>
</widget>
</item>
@@ -92,17 +92,7 @@
<string>Enables Fast GPU Time. This option will force most games to run at their highest native resolution.</string>
</property>
<property name="text">
- <string>Use Fast GPU Time (hack)</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="use_caches_gc">
- <property name="toolTip">
- <string>Enables garbage collection for the GPU caches, this will try to keep VRAM within 3-4 GB by flushing the least used textures/buffers. May cause issues in a few games.</string>
- </property>
- <property name="text">
- <string>Enable GPU cache garbage collection (experimental)</string>
+ <string>Use Fast GPU Time (Hack)</string>
</property>
</widget>
</item>
diff --git a/src/yuzu/configuration/configure_input_player_widget.cpp b/src/yuzu/configuration/configure_input_player_widget.cpp
index cd633e45f..9c890ed5d 100644
--- a/src/yuzu/configuration/configure_input_player_widget.cpp
+++ b/src/yuzu/configuration/configure_input_player_widget.cpp
@@ -647,18 +647,18 @@ void PlayerControlPreview::DrawHandheldController(QPainter& p, const QPointF cen
// 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);
diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_network.cpp
index 4aa424803..ae22f1018 100644
--- a/src/yuzu/configuration/configure_service.cpp
+++ b/src/yuzu/configuration/configure_network.cpp
@@ -5,9 +5,11 @@
#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) {
diff --git a/src/yuzu/configuration/configure_service.h b/src/yuzu/configuration/configure_network.h
index f5c1b703a..442b68e6b 100644
--- a/src/yuzu/configuration/configure_service.h
+++ b/src/yuzu/configuration/configure_network.h
@@ -9,15 +9,15 @@
#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};
};
diff --git a/src/yuzu/configuration/configure_service.ui b/src/yuzu/configuration/configure_network.ui
index 9668dd557..5f9b7e97b 100644
--- a/src/yuzu/configuration/configure_service.ui
+++ b/src/yuzu/configuration/configure_network.ui
@@ -1,7 +1,7 @@
<?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>
@@ -17,21 +17,37 @@
<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>
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp
index 22ca1285d..f89ea8ea7 100644
--- a/src/yuzu/debugger/console.cpp
+++ b/src/yuzu/debugger/console.cpp
@@ -21,6 +21,7 @@ void ToggleConsole() {
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
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index e97804220..f9d949e75 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -515,16 +515,16 @@ void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::stri
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
QAction* open_transferable_shader_cache =
- context_menu.addAction(tr("Open Transferable Shader Cache"));
+ context_menu.addAction(tr("Open Transferable Pipeline Cache"));
context_menu.addSeparator();
QMenu* remove_menu = context_menu.addMenu(tr("Remove"));
QAction* remove_update = remove_menu->addAction(tr("Remove Installed Update"));
QAction* remove_dlc = remove_menu->addAction(tr("Remove All Installed DLC"));
QAction* remove_custom_config = remove_menu->addAction(tr("Remove Custom Configuration"));
- QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Shader Cache"));
- QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Shader Cache"));
+ QAction* remove_gl_shader_cache = remove_menu->addAction(tr("Remove OpenGL Pipeline Cache"));
+ QAction* remove_vk_shader_cache = remove_menu->addAction(tr("Remove Vulkan Pipeline Cache"));
remove_menu->addSeparator();
- QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Shader Caches"));
+ QAction* remove_shader_cache = remove_menu->addAction(tr("Remove All Pipeline Caches"));
QAction* remove_all_content = remove_menu->addAction(tr("Remove All Installed Contents"));
QMenu* dump_romfs_menu = context_menu.addMenu(tr("Dump RomFS"));
QAction* dump_romfs = dump_romfs_menu->addAction(tr("Dump RomFS"));
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 5940e0cfd..e36774cc6 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -175,21 +175,6 @@ void GMainWindow::ShowTelemetryCallout() {
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();
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 464cd472e..891f7be6f 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -468,7 +468,6 @@ void Config::ReadValues() {
ReadSetting("Renderer", Settings::values.nvdec_emulation);
ReadSetting("Renderer", Settings::values.accelerate_astc);
ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
- ReadSetting("Renderer", Settings::values.use_caches_gc);
ReadSetting("Renderer", Settings::values.bg_red);
ReadSetting("Renderer", Settings::values.bg_green);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index c10093820..ba2c993ba 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -74,31 +74,14 @@ static void PrintVersion() {
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;