Compare commits

...

7 Commits

Author SHA1 Message Date
lizzie fb555a03e0 [dynarmic] optimise BlockOfCode to use less memory
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-25 15:42:22 +00:00
lizzie d9336ce6ce [common, settings] Fixes misaligned Aniso levels (#4295)
PR/Commit Owner: CamilleLaVey

Just quick fixes to ANISO bugged level selector, removes x32 and x64 from choices, also fixes the none value to actully use default.

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4295
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-24 00:06:11 +02:00
PavelBARABANOV 4eb8dd1458 partial revert a41a98028a (#4292)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
This commit fix launch Assassin's creed 3

changes nvdrv:a - break launch with fw 20+
changes ldr:ro - break skip loading menu with all fw

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4292
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-08-24 00:01:21 +02:00
PavelBARABANOV 4a3cc9a3c2 [android] Fix Nvdec None (#4288)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Nvdec None is working now; I removed the unnecessary comments — the variable names make everything clear enough.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4288
Reviewed-by: Samuel <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-23 23:58:50 +02:00
Samuel 86ba79b1ba [vulkan, sgsr] Adjustments to SGSR implementation (#4293)
PR/ Commit Owner: CamiileLaVey

This change has specified fixes on the SGSR initial implementation, it brought a wider sharpening level limits and better image quality.

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4293
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
2026-08-23 23:36:24 +02:00
lizzie b610b03d29 [common/logging] eliminate uneeded std::string{} allocations per each logging (#4273)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

the idea is simple, `std::string{}` adds pressure to the memory allocator
so whats the best next thing we can do? well of course use our stack
its plenty, but we shouldn't be greedy either, BUFSIZ should be a fair amount of
space for any would-be messages anyways

the main idea behind this PR is to remove std::string{} allocations
done with libfmt, that way we have 0-allocs per logging entry
this should 100% remove pressure uneeded from the memory allocator
we should only allocate things that are important, we can use our
trusty fast stack for any string manip we need to do
not await/global lock or do evil things with the memory allocator

obviously stack is thread local already sooo... this is even better
than having to deal with malloc()/free() in any capacity whatsoever

and no clang can't heap ellide this (how would you even?)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4273
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-23 22:37:17 +02:00
lizzie 421e0b834c Revert "[dynarmic] Remove Ignore Global Monitor from CPU Accuracy Auto (#3846)" (#4276)
This reverts commit 3a823de605.

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

kills performance and doesn't fix graphical glitches
(according to gido)
"It improved nothing but kills performance on default setting"

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4276
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-08-23 22:10:22 +02:00
41 changed files with 207 additions and 224 deletions
+2 -2
View File
@@ -340,10 +340,10 @@
"version": "vulkan-sdk-%NUMERIC_VERSION%"
},
"xbyak": {
"hash": "b6475276b2faaeb315734ea8f4f8bd87ededcee768961b39679bee547e7f3e98884d8b7851e176d861dab30a80a76e6ea302f8c111483607dde969b4797ea95a",
"hash": "e0aa0a603dd3ac1a39d82213df1e73c042831aec2d6b2fe382c899651eaae4ed7e8aeb6be9c41ea0097087c9d091961ab18f313cd6a9e10d621715ffd49bfe36",
"package": "xbyak",
"repo": "herumi/xbyak",
"version": "v7.35.2"
"version": "v7.40.1"
},
"zlib": {
"hash": "16fea4df307a68cf0035858abe2fd550250618a97590e202037acd18a666f57afc10f8836cbbd472d54a0e76539d0e558cb26f059d53de52ff90634bbf4f47d4",
@@ -624,6 +624,7 @@ abstract class SettingsItem(
IntSetting.FSR_SHARPENING_SLIDER,
titleId = R.string.fsr_sharpness,
descriptionId = R.string.fsr_sharpness_description,
max = 200,
units = "%"
)
)
@@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
container,
IntSetting.FSR_SHARPENING_SLIDER,
minValue = 0,
maxValue = 100,
maxValue = 200,
units = "%"
)
}
@@ -1107,7 +1107,6 @@
<string name="theme_mode_light">فاتح</string>
<string name="theme_mode_dark">داكن</string>
<string name="multiplier_none">لا شيء</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">خلفيات سوداء</string>
@@ -991,7 +991,6 @@ Wirklich fortfahren?</string>
<string name="theme_mode_light">Hell</string>
<string name="theme_mode_dark">Dunkel</string>
<string name="multiplier_none">Keine</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Schwarze Hintergründe</string>
@@ -1092,7 +1092,6 @@
<string name="theme_mode_light">Claro</string>
<string name="theme_mode_dark">Oscuro</string>
<string name="multiplier_none">Nada</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Fondos oscuros</string>
@@ -808,9 +808,6 @@
<string name="multiplier_x4">x4</string>
<string name="multiplier_x8">x8</string>
<string name="multiplier_x16">x16</string>
<string name="multiplier_x32">x32</string>
<string name="multiplier_x64">x64</string>
<string name="multiplier_none">None</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">پس‌زمینه مشکی</string>
@@ -1004,7 +1004,6 @@
<string name="theme_mode_light">Lumineux</string>
<string name="theme_mode_dark">Sombre</string>
<string name="multiplier_none">Aucun</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Arrière-plan noir</string>
@@ -935,7 +935,6 @@
<string name="theme_mode_light">Jasny</string>
<string name="theme_mode_dark">Ciemny</string>
<string name="multiplier_none">Brak</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Czarne tła</string>
@@ -891,7 +891,6 @@
<string name="theme_mode_light">Claro</string>
<string name="theme_mode_dark">Escuro</string>
<string name="multiplier_none">Nenhum</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Planos de fundo pretos</string>
@@ -1071,7 +1071,6 @@
<string name="theme_mode_light">Светлая</string>
<string name="theme_mode_dark">Темная</string>
<string name="multiplier_none">Отключено</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Чёрный фон</string>
@@ -1053,7 +1053,6 @@
<string name="theme_mode_light">Світла</string>
<string name="theme_mode_dark">Темна</string>
<string name="multiplier_none">Жодного</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Чорний фон</string>
@@ -1081,7 +1081,6 @@
<string name="theme_mode_light">浅色</string>
<string name="theme_mode_dark">深色</string>
<string name="multiplier_none"></string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">使用黑色背景</string>
@@ -1006,7 +1006,6 @@
<string name="theme_mode_light">淺色</string>
<string name="theme_mode_dark">深色</string>
<string name="multiplier_none"></string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">黑色背景</string>
+12 -23
View File
@@ -111,43 +111,38 @@
<item>1</item>
</integer-array>
<!-- VRAM USAGE MODE CHOICES -->
<string-array name="vramUsageMethodNames">
<item>@string/vram_usage_conservative</item>
<item>@string/vram_usage_aggressive</item>
</string-array>
<!-- VRAM USAGE MODE VALUES -->
<integer-array name="vramUsageMethodValues">
<item>0</item> <!-- Conservative -->
<item>1</item> <!-- Aggressive -->
<item>0</item>
<item>1</item>
</integer-array>
<!-- ASTC Decoding Method Choices -->
<string-array name="astcDecodingMethodNames">
<item>@string/accelerate_astc_cpu</item>
<item>@string/accelerate_astc_gpu</item>
<item>@string/accelerate_astc_async</item>
</string-array>
<!-- ASTC Decoding Method Values -->
<integer-array name="astcDecodingMethodValues">
<item>0</item> <!-- CPU -->
<item>1</item> <!-- GPU -->
<item>2</item> <!-- CPU Asynchronously -->
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<!-- NVDEC Emulation Choices -->
<string-array name="rendererNvdecNames">
<item>@string/nvdec_emulation_none</item> <!-- Off -->
<item>@string/nvdec_emulation_cpu</item> <!-- Cpu -->
<item>@string/nvdec_emulation_gpu</item> <!-- Gpu -->
<item>@string/nvdec_emulation_none</item>
<item>@string/nvdec_emulation_cpu</item>
<item>@string/nvdec_emulation_gpu</item>
</string-array>
<!-- NVDEC Emulation Values -->
<integer-array name="rendererNvdecValues">
<item>3</item> <!-- Off value -->
<item>1</item> <!-- CPU value -->
<item>2</item> <!-- GPU value -->
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<string-array name="rendererResolutionNames">
@@ -513,9 +508,6 @@
<item>@string/multiplier_x4</item>
<item>@string/multiplier_x8</item>
<item>@string/multiplier_x16</item>
<item>@string/multiplier_x32</item>
<item>@string/multiplier_x64</item>
<item>@string/multiplier_none</item>
</string-array>
<integer-array name="anisoValues">
<item>0</item>
@@ -524,9 +516,6 @@
<item>3</item>
<item>4</item>
<item>5</item>
<item>6</item>
<item>7</item>
<item>8</item>
</integer-array>
<string-array name="verticalAlignmentEntries">
@@ -1246,9 +1246,6 @@
<string name="multiplier_x4" translatable="false">x4</string>
<string name="multiplier_x8" translatable="false">x8</string>
<string name="multiplier_x16" translatable="false">x16</string>
<string name="multiplier_x32" translatable="false">x32</string>
<string name="multiplier_x64" translatable="false">x64</string>
<string name="multiplier_none">None</string>
<!-- Black backgrounds theme -->
<string name="use_black_backgrounds">Black backgrounds</string>
+56 -45
View File
@@ -39,6 +39,19 @@
namespace Common::Log {
/// @brief A log entry. Log entries are store in a structured format to permit more varied output
/// formatting on different frontends, as well as facilitating filtering and aggregation.
struct Entry {
char const* message = nullptr;
size_t message_len = 0;
std::chrono::microseconds timestamp;
Class log_class{};
Level log_level{};
const char* filename = nullptr;
const char* function = nullptr;
uint32_t line_num = 0;
};
namespace {
/// @brief Returns the name of the passed log class as a C-string. Subclasses are separated by periods
@@ -70,8 +83,6 @@ const char* GetLevelName(Level log_level) {
}
}
}
// Some IDEs prefer <file>:<line> instead, so let's just do that :)
std::string FormatLogMessage(const Entry& entry) noexcept {
if (!entry.filename) return "";
@@ -79,10 +90,9 @@ std::string FormatLogMessage(const Entry& entry) noexcept {
auto const time_fractional = uint32_t(entry.timestamp.count() % 1000000);
auto const class_name = GetLogClassName(entry.log_class);
auto const level_name = GetLevelName(entry.log_level);
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional, class_name, level_name, entry.filename, entry.line_num, entry.function, entry.message);
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}\n", time_seconds, time_fractional, class_name, level_name, entry.filename, entry.line_num, entry.function, entry.message);
}
namespace {
template <typename It>
Level GetLevelByName(const It begin, const It end) {
for (u32 i = 0; i < u32(Level::Count); ++i) {
@@ -127,25 +137,6 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
instance.SetClassLevel(log_class, level);
return true;
}
} // Anonymous namespace
void Filter::ParseFilterString(std::string_view filter_view) {
auto clause_begin = filter_view.cbegin();
while (clause_begin != filter_view.cend()) {
auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
// If clause isn't empty
if (clause_end != clause_begin) {
ParseFilterRule(*this, clause_begin, clause_end);
}
if (clause_end != filter_view.cend()) {
// Skip over the whitespace
++clause_end;
}
clause_begin = clause_end;
}
}
namespace {
/// @brief Trims up to and including the last of ../, ..\, src/, src\ in a string
/// do not be fooled this isn't generating new strings on .rodata :)
@@ -208,7 +199,7 @@ struct ColorConsoleBackend final : public Backend {
}());
SetConsoleTextAttribute(console_handle, color);
auto const df = GetDirectFormatArgs(entry);
std::fprintf(stdout, CCB_PRINTF_FMT "\n", df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message.c_str());
std::fprintf(stdout, CCB_PRINTF_FMT "\n", df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
}
}
void Flush() noexcept override {}
@@ -220,22 +211,24 @@ struct ColorConsoleBackend final : public Backend {
~ColorConsoleBackend() noexcept override {}
void Write(const Entry& entry) noexcept override {
if (enabled) {
#define ESC "\x1b"
auto const color_str = [&entry]() -> const char* {
switch (entry.log_level) {
#define CCB_MAKE_COLOR_FMT(X) ESC X CCB_PRINTF_FMT ESC "[0m\n"
case Level::Debug: return CCB_MAKE_COLOR_FMT("[0;36m"); // Cyan
case Level::Info: return CCB_MAKE_COLOR_FMT("[0;37m"); // Bright gray
case Level::Warning: return CCB_MAKE_COLOR_FMT("[1;33m"); // Bright yellow
case Level::Error: return CCB_MAKE_COLOR_FMT("[1;31m"); // Bright red
case Level::Critical: return CCB_MAKE_COLOR_FMT("[1;35m"); // Bright magenta
default: return CCB_MAKE_COLOR_FMT("[1;30m"); // Grey
#undef CCB_MAKE_COLOR_FMT
case Level::Debug: return "[0;36m"; // Cyan
case Level::Info: return "[0;37m"; // Bright gray
case Level::Warning: return "[1;33m"; // Bright yellow
case Level::Error: return "[1;31m"; // Bright red
case Level::Critical: return "[1;35m"; // Bright magenta
default: return "[1;30m"; // Grey
}
}();
auto const df = GetDirectFormatArgs(entry);
std::fprintf(stdout, color_str, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message.c_str());
#undef ESC
// more restrictive, because take for example this simple prelude:
// [ 50.872256] Config <Info> common/settings.cpp:142:LogSettings:
char buffer[128];
auto result = fmt::format_to_n(buffer, sizeof(buffer) - 1, "\x1b{}[{:4d}.{:06d}] {} <{}> {}:{}:{}: ", color_str, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
std::fwrite(buffer, 1, (std::min)(sizeof(buffer) - 1, result.size), stdout);
std::fwrite(entry.message, 1, entry.message_len, stdout);
std::fwrite("\x1b[0m\n", 1, sizeof("\x1b[0m\n"), stdout);
}
}
void Flush() noexcept override {}
@@ -246,7 +239,7 @@ struct ColorConsoleBackend final : public Backend {
#ifndef __OPENORBIS__
/// @brief Backend that writes to a file passed into the constructor
struct FileBackend final : public Backend {
explicit FileBackend(const std::filesystem::path& filename) noexcept {
explicit FileBackend(const std::filesystem::path filename) noexcept {
auto old_filename = filename;
old_filename += ".old.txt";
// Existence checks are done within the functions themselves.
@@ -261,7 +254,7 @@ struct FileBackend final : public Backend {
if (!enabled)
return;
auto message = FormatLogMessage(entry).append(1, '\n');
auto message = FormatLogMessage(entry);
#ifndef __ANDROID__
if (Settings::values.censor_username.GetValue()) {
// This must be a static otherwise it would get checked on EVERY
@@ -269,8 +262,7 @@ struct FileBackend final : public Backend {
static std::string username = []() -> std::string {
// in order of precedence
// LOGNAME usually works on UNIX, USERNAME on Windows
// Some UNIX systems suck and don't use LOGNAME so we also
// need USER :(
// Some UNIX systems suck and don't use LOGNAME so we also need USER :(
for (auto const var : { "LOGNAME", "USERNAME", "USER", })
if (auto const s = ::getenv(var); s != nullptr)
return std::string{s};
@@ -280,7 +272,7 @@ struct FileBackend final : public Backend {
boost::replace_all(message, username, "user");
}
#endif
bytes_written += file->WriteString(message);
bytes_written += file->WriteSpan(std::span<const char>{message.begin(), message.end()});
// Option to log each line rather than 4k buffers
if (Settings::values.log_flush_line.GetValue())
@@ -308,14 +300,13 @@ private:
bool enabled = true;
};
#endif
#ifdef _WIN32
/// @brief Backend that writes to Visual Studio's output window
struct DebuggerBackend final : public Backend {
explicit DebuggerBackend() noexcept = default;
~DebuggerBackend() noexcept override = default;
void Write(const Entry& entry) noexcept override {
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry)).c_str());
}
void Flush() noexcept override {}
};
@@ -338,7 +329,7 @@ struct LogcatBackend : public Backend {
}
}();
auto const df = GetDirectFormatArgs(entry);
__android_log_print(android_log_priority, "YuzuNative", CCB_PRINTF_FMT, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message.c_str());
__android_log_print(android_log_priority, "YuzuNative", CCB_PRINTF_FMT, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
}
void Flush() noexcept override {}
};
@@ -377,7 +368,23 @@ struct Impl {
#endif
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
};
} // namespace
} // Anonymous namespace
void Filter::ParseFilterString(std::string_view filter_view) {
auto clause_begin = filter_view.cbegin();
while (clause_begin < filter_view.cend()) {
auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
// If clause isn't empty
if (clause_end != clause_begin) {
ParseFilterRule(*this, clause_begin, clause_end);
}
if (clause_end != filter_view.cend()) {
// Skip over the whitespace
++clause_end;
}
clause_begin = clause_end;
}
}
// Constructor shall NOT depend upon Settings() or whatever
// it's ran at global static ctor() time... so BE CAREFUL MFER!
@@ -418,10 +425,14 @@ void SetColorConsoleBackendEnabled(bool enabled) {
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) {
if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) {
char buffer[BUFSIZ];
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
buffer[result.size] = '\0';
auto const flush = ::Settings::values.log_flush_line.GetValue();
logging_instance->ForEachBackend([=](Backend& backend) {
backend.Write(Entry{
.message = fmt::vformat(format, args),
.message = buffer,
.message_len = (std::min)(sizeof(buffer) - 1, result.size),
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
.log_class = log_class,
.log_level = log_level,
-21
View File
@@ -140,25 +140,4 @@ void Stop();
void SetGlobalFilter(const Filter& filter);
void SetColorConsoleBackendEnabled(bool enabled);
/// @brief A log entry. Log entries are store in a structured format to permit more varied output
/// formatting on different frontends, as well as facilitating filtering and aggregation.
struct Entry {
std::string message;
std::chrono::microseconds timestamp;
Class log_class{};
Level log_level{};
const char* filename = nullptr;
const char* function = nullptr;
unsigned int line_num = 0;
};
/// Formats a log entry into the provided text buffer.
std::string FormatLogMessage(const Entry& entry) noexcept;
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
void PrintColoredMessage(const Entry& entry) noexcept;
/// Formats and prints a log entry to the android logcat.
void PrintMessageToLogcat(const Entry& entry) noexcept;
} // namespace Common::Log
+1 -1
View File
@@ -128,7 +128,7 @@ ENUM(TimeZone, Auto, Default, Cet, Cst6Cdt, Cuba, Eet, Egypt, Eire, Est, Est5Edt
GmtPlusZero, GmtMinusZero, GmtZero, Greenwich, Hongkong, Hst, Iceland, Iran, Israel, Jamaica,
Japan, Kwajalein, Libya, Met, Mst, Mst7Mdt, Navajo, Nz, NzChat, Poland, Portugal, Prc, Pst8Pdt,
Roc, Rok, Singapore, Turkey, Uct, Universal, Utc, WSu, Wet, Zulu);
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None);
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16);
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120);
@@ -286,6 +286,7 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreStandardFPCRValue;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_InaccurateNaN;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
break;
// Paranoia mode for debugging optimizations
case Settings::CpuAccuracy::Paranoid:
@@ -338,6 +338,7 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
config.unsafe_optimizations = true;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_UnfuseFMA;
config.fastmem_address_space_bits = 64;
config.optimizations |= Dynarmic::OptimizationFlag::Unsafe_IgnoreGlobalMonitor;
break;
// Paranoia mode for debugging optimizations
case Settings::CpuAccuracy::Paranoid:
+6 -1
View File
@@ -105,7 +105,12 @@ void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
auto module = std::make_shared<Module>(system);
server_manager->RegisterNamedService("nvdrv", std::make_shared<NVDRV>(system, module, "nvdrv"));
server_manager->RegisterNamedService("nvdrv:a", std::make_shared<NVDRV>(system, module, "nvdrv:a"));
const auto NvdrvInterfaceFactoryForApplets = [&, module] {
return std::make_shared<NVDRV>(system, module, "nvdrv:a");
};
server_manager->RegisterNamedService("nvdrv:a", NvdrvInterfaceFactoryForApplets);
server_manager->RegisterNamedService("nvdrv:s", std::make_shared<NVDRV>(system, module, "nvdrv:s"));
server_manager->RegisterNamedService("nvdrv:t", std::make_shared<NVDRV>(system, module, "nvdrv:t"));
server_manager->RegisterNamedService("nvmemp", std::make_shared<NVMEMP>(system));
+6 -1
View File
@@ -588,7 +588,12 @@ void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
auto ro = std::make_shared<RoContext>();
server_manager->RegisterNamedService("ldr:ro", std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User));
const auto RoInterfaceFactoryForUser = [&, ro] {
return std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User);
};
server_manager->RegisterNamedService("ldr:ro", std::move(RoInterfaceFactoryForUser));
server_manager->RegisterNamedService("ro:1", std::make_shared<RoInterface>(system, "ro:1", ro, NrrKind::JitPlugin));
server_manager->RegisterNamedService("ro:dmnt", std::make_shared<IDebugMonitorInterface>(system));
ServerManager::RunServer(std::move(server_manager));
@@ -247,7 +247,7 @@ void A32EmitX64::GenTerminalHandlers() {
calculate_location_descriptor();
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)]);
code.sub(eax, 1);
code.and_(eax, u32(A32JitState::RSBPtrMask));
code.and_(eax, u32(A32JitState::RSB_PTR_MASK));
code.mov(dword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_ptr)], eax);
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A32JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
@@ -37,9 +37,9 @@ using namespace Backend::X64;
static RunCodeCallbacks GenRunCodeCallbacks(A32::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg, const A32::UserConfig& conf) {
return RunCodeCallbacks{
std::make_unique<ArgCallback>(LookupBlock, reinterpret_cast<u64>(arg)),
std::make_unique<ArgCallback>(Devirtualize<&A32::UserCallbacks::AddTicks>(cb)),
std::make_unique<ArgCallback>(Devirtualize<&A32::UserCallbacks::GetTicksRemaining>(cb)),
ArgCallback(LookupBlock, reinterpret_cast<u64>(arg)),
ArgCallback(Devirtualize<&A32::UserCallbacks::AddTicks>(cb)),
ArgCallback(Devirtualize<&A32::UserCallbacks::GetTicksRemaining>(cb)),
conf.enable_cycle_counting,
};
}
@@ -79,7 +79,7 @@ struct Jit::Impl {
jit_interface->is_executing = true;
const CodePtr current_codeptr = [this] {
// RSB optimization
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSBPtrMask;
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A32JitState::RSB_PTR_MASK;
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
jit_state.rsb_ptr = new_rsb_ptr;
return reinterpret_cast<CodePtr>(jit_state.rsb_codeptrs[new_rsb_ptr]);
@@ -27,6 +27,9 @@ struct A32JitState {
A32JitState() { ResetRSB(); }
static constexpr std::size_t RSB_SIZE = 8; // MUST be a power of 2.
static constexpr std::size_t RSB_PTR_MASK = RSB_SIZE - 1;
std::array<u32, 16> Reg{}; // Current register file.
// TODO: Mode-specific register sets unimplemented.
@@ -36,8 +39,9 @@ struct A32JitState {
u32 cpsr_q = 0;
u32 cpsr_nzcv = 0;
u32 cpsr_jaifm = 0;
u32 Cpsr() const;
void SetCpsr(u32 cpsr);
u32 fpsr_exc = 0;
u32 fpsr_qc = 0;
u32 fpsr_nzcv = 0;
alignas(16) std::array<u32, 64> ExtReg{}; // Extension registers.
@@ -49,21 +53,19 @@ struct A32JitState {
// Exclusive state
u32 exclusive_state = 0;
static constexpr std::size_t RSBSize = 8; // MUST be a power of 2.
static constexpr std::size_t RSBPtrMask = RSBSize - 1;
u32 rsb_ptr = 0;
std::array<u64, RSBSize> rsb_location_descriptors;
std::array<u64, RSBSize> rsb_codeptrs;
void ResetRSB();
std::array<u64, RSB_SIZE> rsb_location_descriptors;
std::array<u64, RSB_SIZE> rsb_codeptrs;
u32 fpsr_exc = 0;
u32 fpsr_qc = 0;
u32 fpsr_nzcv = 0;
u32 Cpsr() const;
void SetCpsr(u32 cpsr);
void ResetRSB();
u32 Fpscr() const;
void SetFpscr(u32 FPSCR);
u64 GetUniqueHash() const noexcept {
return (static_cast<u64>(upper_location_descriptor) << 32) | (static_cast<u64>(Reg[15]));
return (u64(upper_location_descriptor) << 32) | (u64(Reg[15]));
}
void TransferJitState(const A32JitState& src, bool reset_rsb) {
@@ -208,7 +208,7 @@ void A64EmitX64::GenTerminalHandlers() {
calculate_location_descriptor();
code.mov(eax, dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)]);
code.sub(eax, 1);
code.and_(eax, u32(A64JitState::RSBPtrMask));
code.and_(eax, u32(A64JitState::RSB_PTR_MASK));
code.mov(dword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_ptr)], eax);
code.cmp(rbx, qword[code.ABI_JIT_PTR + offsetof(A64JitState, rsb_location_descriptors) + rax * sizeof(u64)]);
if (conf.HasOptimization(OptimizationFlag::FastDispatch)) {
@@ -33,9 +33,9 @@ using namespace Backend::X64;
static RunCodeCallbacks GenRunCodeCallbacks(A64::UserCallbacks* cb, CodePtr (*LookupBlock)(void* lookup_block_arg), void* arg, const A64::UserConfig& conf) {
return RunCodeCallbacks{
std::make_unique<ArgCallback>(LookupBlock, reinterpret_cast<u64>(arg)),
std::make_unique<ArgCallback>(Devirtualize<&A64::UserCallbacks::AddTicks>(cb)),
std::make_unique<ArgCallback>(Devirtualize<&A64::UserCallbacks::GetTicksRemaining>(cb)),
ArgCallback(LookupBlock, reinterpret_cast<u64>(arg)),
ArgCallback(Devirtualize<&A64::UserCallbacks::AddTicks>(cb)),
ArgCallback(Devirtualize<&A64::UserCallbacks::GetTicksRemaining>(cb)),
conf.enable_cycle_counting,
};
}
@@ -78,7 +78,7 @@ public:
// TODO: Check code alignment
const CodePtr current_code_ptr = [this] {
// RSB optimization
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSBPtrMask;
const u32 new_rsb_ptr = (jit_state.rsb_ptr - 1) & A64JitState::RSB_PTR_MASK;
if (jit_state.GetUniqueHash() == jit_state.rsb_location_descriptors[new_rsb_ptr]) {
jit_state.rsb_ptr = new_rsb_ptr;
return CodePtr(jit_state.rsb_codeptrs[new_rsb_ptr]);
@@ -29,18 +29,19 @@ struct A64JitState {
A64JitState() { ResetRSB(); }
// Exclusive state stuff
static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull;
// Return stack buffer
static constexpr size_t RSB_SIZE = 8; // MUST be a power of 2.
static constexpr size_t RSB_PTR_MASK = RSB_SIZE - 1;
std::array<u64, 31> reg{};
u64 sp = 0;
u64 pc = 0;
u32 cpsr_nzcv = 0;
u32 GetPstate() const {
return NZCV::FromX64(cpsr_nzcv);
}
void SetPstate(u32 new_pstate) {
cpsr_nzcv = NZCV::ToX64(new_pstate);
}
u32 fpsr_exc = 0;
u32 fpsr_qc = 0;
u32 fpcr = 0;
alignas(16) std::array<u64, 64> vec{}; // Extension registers.
@@ -50,29 +51,31 @@ struct A64JitState {
volatile u32 halt_reason = 0;
// Exclusive state
static constexpr u64 RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFF0ull;
u8 exclusive_state = 0;
static constexpr size_t RSBSize = 8; // MUST be a power of 2.
static constexpr size_t RSBPtrMask = RSBSize - 1;
u32 rsb_ptr = 0;
std::array<u64, RSBSize> rsb_location_descriptors;
std::array<u64, RSBSize> rsb_codeptrs;
std::array<u64, RSB_SIZE> rsb_location_descriptors;
std::array<u64, RSB_SIZE> rsb_codeptrs;
u32 GetPstate() const {
return NZCV::FromX64(cpsr_nzcv);
}
void SetPstate(u32 new_pstate) {
cpsr_nzcv = NZCV::ToX64(new_pstate);
}
void ResetRSB() {
rsb_location_descriptors.fill(0xFFFFFFFFFFFFFFFFull);
rsb_codeptrs.fill(0);
}
u32 fpsr_exc = 0;
u32 fpsr_qc = 0;
u32 fpcr = 0;
u32 GetFpcr() const;
u32 GetFpsr() const;
void SetFpcr(u32 value);
void SetFpsr(u32 value);
u64 GetUniqueHash() const noexcept {
const u64 fpcr_u64 = static_cast<u64>(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
const u64 fpcr_u64 = u64(fpcr & A64::LocationDescriptor::fpcr_mask) << A64::LocationDescriptor::fpcr_shift;
const u64 pc_u64 = pc & A64::LocationDescriptor::pc_mask;
return pc_u64 | fpcr_u64;
}
@@ -145,11 +145,9 @@ void ProtectMemory(const void* base, size_t size, bool is_executable) {
HostFeature GetHostFeatures() {
HostFeature features = {};
#ifdef DYNARMIC_ENABLE_CPU_FEATURE_DETECTION
using Cpu = Xbyak::util::Cpu;
Xbyak::util::Cpu cpu_info;
Xbyak::util::Cpu cpu_info{};
if (cpu_info.has(Cpu::tSSSE3))
features |= HostFeature::SSSE3;
if (cpu_info.has(Cpu::tSSE41))
@@ -196,7 +194,6 @@ HostFeature GetHostFeatures() {
features |= HostFeature::GFNI;
if (cpu_info.has(Cpu::tWAITPKG))
features |= HostFeature::WAITPKG;
if (cpu_info.has(Cpu::tBMI2)) {
// BMI2 instructions such as pdep and pext have been very slow up until Zen 3.
// Check for Zen 3 or newer by its family (0x19).
@@ -214,7 +211,6 @@ HostFeature GetHostFeatures() {
}
}
#endif
return features;
}
@@ -233,23 +229,27 @@ bool IsUnderRosetta() {
} // anonymous namespace
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
static const auto default_cg_mode = Xbyak::DontSetProtectRWE;
#else
static const auto default_cg_mode = nullptr; //Allow RWE
#endif
BlockOfCode::BlockOfCode(RunCodeCallbacks cb, JitStateInfo jsi, size_t total_code_size, std::function<void(BlockOfCode&)> rcp)
: Xbyak::CodeGenerator(total_code_size, default_cg_mode, &s_allocator)
, cb(std::move(cb))
, jsi(jsi)
, constant_pool(*this, CONSTANT_POOL_SIZE)
, host_features(GetHostFeatures()) {
: Xbyak::CodeGenerator(total_code_size
#ifdef DYNARMIC_ENABLE_NO_EXECUTE_SUPPORT
, Xbyak::DontSetProtectRWE
#else
, nullptr //Allow RWE
#endif
, &s_allocator)
, constant_pool(*this, CONSTANT_POOL_SIZE)
, jsi(jsi)
, cb(std::move(cb))
{
EnableWriting();
EnsureMemoryCommitted(PRELUDE_COMMIT_SIZE);
GenRunCode(rcp);
}
bool BlockOfCode::HasHostFeature(HostFeature feature) const noexcept {
return (GetHostFeatures() & feature) == feature;
}
void BlockOfCode::PreludeComplete() {
prelude_complete = true;
code_begin = getCurr();
@@ -341,7 +341,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
mov(rbx, ABI_PARAM2); // save temporarily in non-volatile register
if (cb.enable_cycle_counting) {
cb.GetTicksRemaining->EmitCall(*this);
cb.GetTicksRemaining.EmitCall(*this);
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], ABI_RETURN);
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
}
@@ -388,7 +388,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
cmp(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], 0);
jng(return_to_caller);
}
cb.LookupBlock->EmitCall(*this);
cb.LookupBlock.EmitCall(*this);
jmp(ABI_RETURN);
align();
@@ -401,7 +401,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
jng(return_to_caller_mxcsr_already_exited);
}
SwitchMxcsrOnEntry();
cb.LookupBlock->EmitCall(*this);
cb.LookupBlock.EmitCall(*this);
jmp(ABI_RETURN);
align();
@@ -415,7 +415,7 @@ void BlockOfCode::GenRunCode(std::function<void(BlockOfCode&)> rcp) {
L(return_to_caller_mxcsr_already_exited);
if (cb.enable_cycle_counting) {
cb.AddTicks->EmitCall(*this, [this](RegList param) {
cb.AddTicks.EmitCall(*this, [this](RegList param) {
mov(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)]);
sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]);
});
@@ -455,18 +455,18 @@ void BlockOfCode::UpdateTicks() {
return;
}
cb.AddTicks->EmitCall(*this, [this](RegList param) {
cb.AddTicks.EmitCall(*this, [this](RegList param) {
mov(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)]);
sub(param[0], qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)]);
});
cb.GetTicksRemaining->EmitCall(*this);
cb.GetTicksRemaining.EmitCall(*this);
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_to_run)], ABI_RETURN);
mov(qword[rsp + ABI_SHADOW_SPACE + offsetof(StackLayout, cycles_remaining)], ABI_RETURN);
}
void BlockOfCode::LookupBlock() {
cb.LookupBlock->EmitCall(*this);
cb.LookupBlock.EmitCall(*this);
}
void BlockOfCode::LoadRequiredFlagsForCondFromRax(IR::Cond cond) {
@@ -520,7 +520,7 @@ void BlockOfCode::LoadRequiredFlagsForCondFromRax(IR::Cond cond) {
}
Xbyak::Address BlockOfCode::Const(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
return constant_pool.GetConstant(frame, lower, upper);
return constant_pool.GetConstant(*this, frame, lower, upper);
}
CodePtr BlockOfCode::GetCodeBegin() const {
@@ -31,9 +31,9 @@ namespace Dynarmic::Backend::X64 {
using CodePtr = const void*;
struct RunCodeCallbacks {
std::unique_ptr<Callback> LookupBlock;
std::unique_ptr<Callback> AddTicks;
std::unique_ptr<Callback> GetTicksRemaining;
ArgCallback LookupBlock;
ArgCallback AddTicks;
ArgCallback GetTicksRemaining;
bool enable_cycle_counting;
};
@@ -166,27 +166,24 @@ public:
JitStateInfo GetJitStateInfo() const { return jsi; }
bool HasHostFeature(HostFeature feature) const {
return (host_features & feature) == feature;
}
bool HasHostFeature(HostFeature feature) const noexcept;
private:
using RunCodeFuncType = HaltReason (*)(void*, CodePtr);
static constexpr size_t MXCSR_ALREADY_EXITED = 1 << 0;
static constexpr size_t FORCE_RETURN = 1 << 1;
RunCodeCallbacks cb;
ConstantPool constant_pool;
JitStateInfo jsi;
std::array<const void*, 4> return_from_run_code;
RunCodeCallbacks cb;
CodePtr code_begin = nullptr;
RunCodeFuncType run_code = nullptr;
RunCodeFuncType step_code = nullptr;
#ifdef _WIN32
size_t committed_size = 0;
#endif
ConstantPool constant_pool;
RunCodeFuncType run_code = nullptr;
RunCodeFuncType step_code = nullptr;
std::array<const void*, 4> return_from_run_code;
bool prelude_complete = false;
const HostFeature host_features;
void GenRunCode(std::function<void(BlockOfCode&)> rcp);
};
@@ -16,8 +16,7 @@
namespace Dynarmic::Backend::X64 {
ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
: code(code)
, insertion_point(0)
: insertion_point(0)
{
code.EnsureMemoryCommitted(align_size + size);
code.int3();
@@ -25,17 +24,17 @@ ConstantPool::ConstantPool(BlockOfCode& code, size_t size)
pool = std::span<ConstantT>(reinterpret_cast<ConstantT*>(code.AllocateFromCodeSpace(size)), size / align_size);
}
Xbyak::Address ConstantPool::GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
Xbyak::Address ConstantPool::GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lower, u64 upper) {
const auto constant = ConstantT(lower, upper);
auto iter = constant_info.find(constant);
if (iter == constant_info.end()) {
auto it = constant_info.find(constant);
if (it == constant_info.end()) {
ASSERT(insertion_point < pool.size());
ConstantT& target_constant = pool[insertion_point];
target_constant = constant;
iter = constant_info.insert({constant, &target_constant}).first;
it = constant_info.insert({constant, &target_constant}).first;
++insertion_point;
}
return frame[code.rip + iter->second];
return frame[code.rip + it->second];
}
} // namespace Dynarmic::Backend::X64
@@ -29,7 +29,7 @@ class ConstantPool final {
public:
ConstantPool(BlockOfCode& code, size_t size);
Xbyak::Address GetConstant(const Xbyak::AddressFrame& frame, u64 lower, u64 upper = 0);
Xbyak::Address GetConstant(BlockOfCode& code, const Xbyak::AddressFrame& frame, u64 lower, u64 upper = 0);
private:
static constexpr size_t align_size = 16; // bytes
@@ -45,7 +45,6 @@ private:
ankerl::unordered_dense::map<ConstantT, void*, ConstantHash> constant_info;
std::span<ConstantT> pool;
BlockOfCode& code;
std::size_t insertion_point;
};
@@ -12,7 +12,7 @@
namespace Dynarmic::Backend::X64 {
enum class HostFeature : u64 {
enum class HostFeature : u32 {
SSSE3 = 1ULL << 0,
SSE41 = 1ULL << 1,
SSE42 = 1ULL << 2,
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2016 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -15,7 +18,7 @@ struct JitStateInfo {
: offsetof_guest_MXCSR(offsetof(JitStateType, guest_MXCSR))
, offsetof_asimd_MXCSR(offsetof(JitStateType, asimd_MXCSR))
, offsetof_rsb_ptr(offsetof(JitStateType, rsb_ptr))
, rsb_ptr_mask(JitStateType::RSBPtrMask)
, rsb_ptr_mask(JitStateType::RSB_PTR_MASK)
, offsetof_rsb_location_descriptors(offsetof(JitStateType, rsb_location_descriptors))
, offsetof_rsb_codeptrs(offsetof(JitStateType, rsb_codeptrs))
, offsetof_cpsr_nzcv(offsetof(JitStateType, cpsr_nzcv))
@@ -522,9 +522,6 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QObject* parent) {
PAIR(AnisotropyMode, X4, tr("4x")),
PAIR(AnisotropyMode, X8, tr("8x")),
PAIR(AnisotropyMode, X16, tr("16x")),
PAIR(AnisotropyMode, X32, tr("32x")),
PAIR(AnisotropyMode, X64, tr("64x")),
PAIR(AnisotropyMode, None, tr("None")),
}});
translations->insert(
{Settings::EnumMetadata<Settings::Language>::Index(),
@@ -7,6 +7,7 @@ layout(push_constant) uniform constants {
vec2 scale;
vec2 size;
vec2 resize_factor;
vec2 crop_offset;
float edge_sharpness;
};
layout(location = 0) out highp vec2 texcoord;
@@ -15,5 +16,5 @@ void main() {
float x = float((gl_VertexIndex & 1) << 2);
float y = float((gl_VertexIndex & 2) << 1);
gl_Position = vec4(x - 1.0f, y - 1.0f, 0.0, 1.0f) * vec4(sign(resize_factor), 1.f, 1.f);
texcoord = vec2(x, y) * abs(resize_factor) * 0.5;
texcoord = crop_offset + vec2(x, y) * abs(resize_factor) * 0.5;
}
@@ -14,6 +14,7 @@ layout(push_constant) uniform constants {
vec2 scale;
vec2 size;
vec2 resize_factor;
vec2 crop_offset;
float edge_sharpness;
};
layout(set = 0, binding = 0) uniform sampler2D sampler0;
@@ -13,6 +13,7 @@
layout( push_constant ) uniform constants {
vec4 ViewportInfo[1];
vec2 ResizeFactor;
vec2 CropOffset;
float EdgeSharpness;
};
layout(set = 0, binding = 0) uniform sampler2D ps0;
+18 -14
View File
@@ -1,6 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/settings.h"
@@ -17,7 +19,7 @@
namespace Vulkan {
using PushConstants = std::array<u32, 4 + 2 + 1>;
using PushConstants = std::array<u32, 4 + 2 + 2 + 1>;
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
: m_memory_allocator{memory_allocator}
@@ -100,26 +102,28 @@ VkImageView SGSR::Draw(const Device& device, Scheduler& scheduler, size_t image_
const f32 input_image_width = f32(input_image_extent.width);
const f32 input_image_height = f32(input_image_extent.height);
const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width;
const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
// expected [0, 2]
const f32 sharpening = f32(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f;
const f32 crop_width = (crop_rect.right - crop_rect.left) * input_image_width;
const f32 crop_height = (crop_rect.bottom - crop_rect.top) * input_image_height;
static constexpr f32 EDGE_SHARPNESS_MAX = 2.0f;
const f32 edge_sharpness =
EDGE_SHARPNESS_MAX - f32(Settings::values.fsr_sharpening_slider.GetValue()) / 200.0f;
// p = (tex * viewport) / input = [0,n] (normalized texcoords)
// p * input = [0,1024], [0,768]
// layout( push_constant ) uniform constants {
// highp vec4 ViewportInfo[1];
// highp vec2 ResizeFactor;
// highp vec2 CropOffset;
// highp float EdgeSharpness;
// };
PushConstants viewport_con{};
viewport_con[0] = std::bit_cast<u32>(std::abs(1.f / viewport_width));
viewport_con[1] = std::bit_cast<u32>(std::abs(1.f / viewport_height));
viewport_con[2] = std::bit_cast<u32>(std::abs(viewport_width));
viewport_con[3] = std::bit_cast<u32>(std::abs(viewport_height));
viewport_con[4] = std::bit_cast<u32>(viewport_width / input_image_width);
viewport_con[5] = std::bit_cast<u32>(viewport_height / input_image_height);
viewport_con[6] = std::bit_cast<u32>(sharpening);
viewport_con[0] = std::bit_cast<u32>(1.f / input_image_width);
viewport_con[1] = std::bit_cast<u32>(1.f / input_image_height);
viewport_con[2] = std::bit_cast<u32>(input_image_width);
viewport_con[3] = std::bit_cast<u32>(input_image_height);
viewport_con[4] = std::bit_cast<u32>(crop_width / input_image_width);
viewport_con[5] = std::bit_cast<u32>(crop_height / input_image_height);
viewport_con[6] = std::bit_cast<u32>((std::min)(crop_rect.left, crop_rect.right));
viewport_con[7] = std::bit_cast<u32>((std::min)(crop_rect.top, crop_rect.bottom));
viewport_con[8] = std::bit_cast<u32>(edge_sharpness);
UploadImages(device, scheduler);
UpdateDescriptorSets(device, source_image_view, image_index);
+9 -9
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
@@ -80,16 +80,16 @@ float TSCEntry::MaxAnisotropy() const noexcept {
case Settings::AnisotropyMode::X4:
case Settings::AnisotropyMode::X8:
case Settings::AnisotropyMode::X16:
case Settings::AnisotropyMode::X32:
case Settings::AnisotropyMode::X64:
added_anisotropic = u32(anisotropic_settings) - 1U;
added_anisotropic = s32(anisotropic_settings) - 1;
break;
case Settings::AnisotropyMode::Automatic:
added_anisotropic = Settings::values.resolution_info.up_scale >> Settings::values.resolution_info.down_shift;
added_anisotropic = (std::max)(added_anisotropic - 1U, 0U);
case Settings::AnisotropyMode::Automatic: {
const u32 resolution_scale = Settings::values.resolution_info.up_scale >>
Settings::values.resolution_info.down_shift;
if (resolution_scale > 1U) {
added_anisotropic = static_cast<s32>(resolution_scale - 1U);
}
break;
case Settings::AnisotropyMode::None:
return 1.0f; //No use of anisotropy
}
}
return float(1U << (max_anisotropy + added_anisotropic));
}