mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-28 09:41:36 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86be89ed46 | |||
| c9c59407ca |
-1
@@ -624,7 +624,6 @@ 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 = 200,
|
||||
maxValue = 100,
|
||||
units = "%"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1107,6 +1107,7 @@
|
||||
<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,6 +991,7 @@ 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,6 +1092,7 @@
|
||||
<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,6 +808,9 @@
|
||||
<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,6 +1004,7 @@
|
||||
<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,6 +935,7 @@
|
||||
<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,6 +891,7 @@
|
||||
<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,6 +1071,7 @@
|
||||
<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,6 +1053,7 @@
|
||||
<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,6 +1081,7 @@
|
||||
<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,6 +1006,7 @@
|
||||
<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>
|
||||
|
||||
@@ -111,38 +111,43 @@
|
||||
<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>
|
||||
<item>1</item>
|
||||
<item>0</item> <!-- Conservative -->
|
||||
<item>1</item> <!-- Aggressive -->
|
||||
</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>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>0</item> <!-- CPU -->
|
||||
<item>1</item> <!-- GPU -->
|
||||
<item>2</item> <!-- CPU Asynchronously -->
|
||||
</integer-array>
|
||||
|
||||
<!-- NVDEC Emulation Choices -->
|
||||
<string-array name="rendererNvdecNames">
|
||||
<item>@string/nvdec_emulation_none</item>
|
||||
<item>@string/nvdec_emulation_cpu</item>
|
||||
<item>@string/nvdec_emulation_gpu</item>
|
||||
<item>@string/nvdec_emulation_none</item> <!-- Off -->
|
||||
<item>@string/nvdec_emulation_cpu</item> <!-- Cpu -->
|
||||
<item>@string/nvdec_emulation_gpu</item> <!-- Gpu -->
|
||||
</string-array>
|
||||
|
||||
<!-- NVDEC Emulation Values -->
|
||||
<integer-array name="rendererNvdecValues">
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>3</item> <!-- Off value -->
|
||||
<item>1</item> <!-- CPU value -->
|
||||
<item>2</item> <!-- GPU value -->
|
||||
</integer-array>
|
||||
|
||||
<string-array name="rendererResolutionNames">
|
||||
@@ -508,6 +513,9 @@
|
||||
<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>
|
||||
@@ -516,6 +524,9 @@
|
||||
<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,6 +1246,9 @@
|
||||
<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>
|
||||
|
||||
+45
-56
@@ -39,19 +39,6 @@
|
||||
|
||||
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
|
||||
@@ -83,6 +70,8 @@ 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 "";
|
||||
@@ -90,9 +79,10 @@ 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}] {} <{}> {}:{}:{}: {}\n", time_seconds, time_fractional, class_name, level_name, entry.filename, entry.line_num, entry.function, entry.message);
|
||||
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", 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) {
|
||||
@@ -137,6 +127,25 @@ 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 :)
|
||||
@@ -199,7 +208,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);
|
||||
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());
|
||||
}
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
@@ -211,24 +220,22 @@ 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) {
|
||||
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
|
||||
#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
|
||||
}
|
||||
}();
|
||||
auto const df = GetDirectFormatArgs(entry);
|
||||
// more restrictive, because take for example this simple prelude:
|
||||
// [ 50.872256] Config <Info> common/settings.cpp:142:LogSettings:
|
||||
char buffer[256];
|
||||
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);
|
||||
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
|
||||
}
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
@@ -239,7 +246,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.
|
||||
@@ -254,7 +261,7 @@ struct FileBackend final : public Backend {
|
||||
if (!enabled)
|
||||
return;
|
||||
|
||||
auto message = FormatLogMessage(entry);
|
||||
auto message = FormatLogMessage(entry).append(1, '\n');
|
||||
#ifndef __ANDROID__
|
||||
if (Settings::values.censor_username.GetValue()) {
|
||||
// This must be a static otherwise it would get checked on EVERY
|
||||
@@ -262,7 +269,8 @@ 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};
|
||||
@@ -272,7 +280,7 @@ struct FileBackend final : public Backend {
|
||||
boost::replace_all(message, username, "user");
|
||||
}
|
||||
#endif
|
||||
bytes_written += file->WriteSpan(std::span<const char>{message.begin(), message.end()});
|
||||
bytes_written += file->WriteString(message);
|
||||
|
||||
// Option to log each line rather than 4k buffers
|
||||
if (Settings::values.log_flush_line.GetValue())
|
||||
@@ -300,13 +308,14 @@ 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)).c_str());
|
||||
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
};
|
||||
@@ -329,7 +338,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);
|
||||
__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());
|
||||
}
|
||||
void Flush() noexcept override {}
|
||||
};
|
||||
@@ -368,23 +377,7 @@ struct Impl {
|
||||
#endif
|
||||
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
|
||||
};
|
||||
} // 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
|
||||
|
||||
// Constructor shall NOT depend upon Settings() or whatever
|
||||
// it's ran at global static ctor() time... so BE CAREFUL MFER!
|
||||
@@ -426,13 +419,9 @@ 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)) {
|
||||
auto const flush = ::Settings::values.log_flush_line.GetValue();
|
||||
char buffer[BUFSIZ];
|
||||
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
|
||||
buffer[(std::min)(result.size, sizeof(buffer) - 1)] = '\0';
|
||||
logging_instance->ForEachBackend([=](Backend& backend) {
|
||||
backend.Write(Entry{
|
||||
.message = buffer,
|
||||
.message_len = (std::min)(result.size, sizeof(buffer) - 1),
|
||||
.message = fmt::vformat(format, args),
|
||||
.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,
|
||||
|
||||
@@ -140,4 +140,25 @@ 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
|
||||
|
||||
@@ -132,9 +132,11 @@ void LogSettings() {
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_INFO(Config, "Eden Configuration:");
|
||||
|
||||
std::string settings_str{};
|
||||
for (auto const& e : settings_list)
|
||||
LOG_INFO(Config, "{}", e);
|
||||
settings_str += e;
|
||||
LOG_INFO(Config, "Eden Configuration:\n{}", settings_str);
|
||||
#define LOG_PATH(NAME) \
|
||||
LOG_INFO(Config, #NAME ": {}", Common::FS::PathToUTF8String(Common::FS::GetEdenPath(Common::FS::EdenPath::NAME)))
|
||||
LOG_PATH(CacheDir);
|
||||
@@ -369,7 +371,7 @@ void TranslateResolutionInfo(ResolutionSetup setup, ResolutionScalingInfo& info)
|
||||
}
|
||||
info.up_factor = static_cast<f32>(info.up_scale) / (1U << info.down_shift);
|
||||
info.down_factor = static_cast<f32>(1U << info.down_shift) / info.up_scale;
|
||||
info.active = true;
|
||||
info.active = info.up_scale != 1 || info.down_shift != 0;
|
||||
}
|
||||
|
||||
void UpdateRescalingInfo() {
|
||||
|
||||
@@ -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);
|
||||
ENUM(AnisotropyMode, Automatic, Default, X2, X4, X8, X16, X32, X64, None);
|
||||
ENUM(AstcDecodeMode, Cpu, Gpu, CpuAsynchronous);
|
||||
ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
|
||||
ENUM(FramePacingMode, Target_Auto, Target_30, Target_60, Target_90, Target_120);
|
||||
|
||||
@@ -286,7 +286,6 @@ 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,7 +338,6 @@ 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:
|
||||
|
||||
@@ -17,56 +17,12 @@
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
constexpr auto MAX_MSG_TIME = std::chrono::milliseconds(250);
|
||||
const auto MAX_MSG_SIZE = 0x1000;
|
||||
|
||||
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
|
||||
Result OutputDebugString(Core::System& system, u64 address, u64 len) {
|
||||
static struct DebugFlusher {
|
||||
std::string msg_buffer;
|
||||
std::mutex msg_mutex;
|
||||
std::condition_variable msg_cv;
|
||||
std::chrono::steady_clock::time_point last_msg_time;
|
||||
std::optional<std::jthread> thread;
|
||||
} flusher_data;
|
||||
R_SUCCEED_IF(len == 0);
|
||||
// Only start the thread the very first time this function is called
|
||||
if (!flusher_data.thread) {
|
||||
flusher_data.thread.emplace([](std::stop_token stop_token) {
|
||||
while (!stop_token.stop_requested()) {
|
||||
std::unique_lock lock(flusher_data.msg_mutex);
|
||||
flusher_data.msg_cv.wait(lock, [&stop_token] {
|
||||
return !flusher_data.msg_buffer.empty() || stop_token.stop_requested();
|
||||
});
|
||||
if (stop_token.stop_requested() && flusher_data.msg_buffer.empty())
|
||||
break;
|
||||
auto timeout = flusher_data.last_msg_time + MAX_MSG_TIME;
|
||||
bool woke_early = flusher_data.msg_cv.wait_until(lock, timeout, [&stop_token] {
|
||||
return flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested();
|
||||
});
|
||||
if (!woke_early || flusher_data.msg_buffer.size() >= MAX_MSG_SIZE || stop_token.stop_requested()) {
|
||||
if (!flusher_data.msg_buffer.empty()) {
|
||||
// Remove trailing newline as LOG_INFO adds that anyways
|
||||
if (flusher_data.msg_buffer.back() == '\n')
|
||||
flusher_data.msg_buffer.pop_back();
|
||||
|
||||
LOG_INFO(Debug_Emulated, "\n{}", flusher_data.msg_buffer);
|
||||
flusher_data.msg_buffer.clear();
|
||||
}
|
||||
if (stop_token.stop_requested()) break;
|
||||
}
|
||||
}
|
||||
flusher_data.msg_cv.notify_all();
|
||||
});
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(flusher_data.msg_mutex);
|
||||
const auto old_size = flusher_data.msg_buffer.size();
|
||||
flusher_data.msg_buffer.resize(old_size + len);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, flusher_data.msg_buffer.data() + old_size, len);
|
||||
flusher_data.last_msg_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
flusher_data.msg_cv.notify_one();
|
||||
std::string msg_buffer(len, 0);
|
||||
GetCurrentMemory(system.Kernel()).ReadBlock(address, msg_buffer.data(), len);
|
||||
LOG_INFO(Debug_Emulated, "{}", msg_buffer);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -105,12 +105,7 @@ 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"));
|
||||
|
||||
const auto NvdrvInterfaceFactoryForApplets = [&, module] {
|
||||
return std::make_shared<NVDRV>(system, module, "nvdrv:a");
|
||||
};
|
||||
|
||||
server_manager->RegisterNamedService("nvdrv:a", NvdrvInterfaceFactoryForApplets);
|
||||
server_manager->RegisterNamedService("nvdrv:a", std::make_shared<NVDRV>(system, module, "nvdrv:a"));
|
||||
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));
|
||||
|
||||
@@ -588,12 +588,7 @@ void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
|
||||
auto ro = std::make_shared<RoContext>();
|
||||
|
||||
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("ldr:ro", std::make_shared<RoInterface>(system, "ldr:ro", ro, NrrKind::User));
|
||||
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));
|
||||
|
||||
@@ -522,6 +522,9 @@ 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(),
|
||||
|
||||
@@ -335,7 +335,7 @@ void SetupDenormControl(const Profile& profile, const IR::Program& program, Emit
|
||||
if (info.uses_fp32_denorms_flush && info.uses_fp32_denorms_preserve) {
|
||||
LOG_DEBUG(Shader_SPIRV, "Fp32 denorm flush and preserve on the same shader");
|
||||
} else if (info.uses_fp32_denorms_flush) {
|
||||
if (profile.support_fp32_denorm_flush && !profile.has_broken_fp32_denorm_flush) {
|
||||
if (profile.support_fp32_denorm_flush) {
|
||||
ctx.AddCapability(spv::Capability::DenormFlushToZero);
|
||||
ctx.AddExecutionMode(main_func, spv::ExecutionMode::DenormFlushToZero, 32U);
|
||||
} else {
|
||||
|
||||
@@ -86,8 +86,6 @@ struct Profile {
|
||||
bool has_broken_signed_operations{};
|
||||
/// Float controls break when fp16 is enabled
|
||||
bool has_broken_fp16_float_controls{};
|
||||
/// Declaring fp32 denorm flush to zero miscompiles on some drivers
|
||||
bool has_broken_fp32_denorm_flush{};
|
||||
/// Dynamic vec4 indexing is broken on some OpenGL drivers
|
||||
bool has_gl_component_indexing_bug{};
|
||||
/// The precise type qualifier is broken in the fragment stage of some drivers
|
||||
|
||||
@@ -206,40 +206,6 @@ foreach(VARIANT IN ITEMS ${SHADER_TYPE_VARIANTS})
|
||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
||||
endforeach()
|
||||
|
||||
set(SHADER_DEFINE_VARIANTS
|
||||
"block_linear_unswizzle_2d.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
||||
"pitch_unswizzle.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
||||
"block_linear_unswizzle_3d.comp|nonarrow|HAS_EXTENDED_TYPES=0"
|
||||
)
|
||||
|
||||
foreach(VARIANT IN ITEMS ${SHADER_DEFINE_VARIANTS})
|
||||
string(REPLACE "|" ";" VARIANT_PARTS ${VARIANT})
|
||||
list(GET VARIANT_PARTS 0 VARIANT_FILENAME)
|
||||
list(GET VARIANT_PARTS 1 VARIANT_SUFFIX)
|
||||
list(GET VARIANT_PARTS 2 VARIANT_DEFINE)
|
||||
|
||||
set(VARIANT_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/${VARIANT_FILENAME})
|
||||
get_filename_component(VARIANT_STEM ${VARIANT_FILENAME} NAME_WE)
|
||||
get_filename_component(VARIANT_EXT ${VARIANT_FILENAME} EXT)
|
||||
string(REPLACE "." "" VARIANT_EXT ${VARIANT_EXT})
|
||||
set(VARIANT_NAME ${VARIANT_STEM}_${VARIANT_SUFFIX}_${VARIANT_EXT})
|
||||
|
||||
string(TOUPPER ${VARIANT_NAME}_SPV VARIANT_VARIABLE_NAME)
|
||||
set(VARIANT_HEADER_FILE ${SHADER_DIR}/${VARIANT_NAME}_spv.h)
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${VARIANT_HEADER_FILE}
|
||||
COMMAND
|
||||
${GLSLANGVALIDATOR} -V ${QUIET_FLAG} -I"${FIDELITYFX_INCLUDE_DIR}" ${GLSL_FLAGS}
|
||||
-D${VARIANT_DEFINE}
|
||||
--variable-name ${VARIANT_VARIABLE_NAME} -o ${VARIANT_HEADER_FILE} ${VARIANT_SOURCE}
|
||||
--target-env ${SPIR_V_VERSION}
|
||||
MAIN_DEPENDENCY
|
||||
${VARIANT_SOURCE}
|
||||
)
|
||||
set(SHADER_HEADERS ${SHADER_HEADERS} ${VARIANT_HEADER_FILE})
|
||||
endforeach()
|
||||
|
||||
foreach(FILEPATH IN ITEMS ${FIDELITYFX_FILES})
|
||||
get_filename_component(FILENAME ${FILEPATH} NAME)
|
||||
string(REPLACE "." "_" HEADER_NAME ${FILENAME})
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
|
||||
#ifdef VULKAN
|
||||
|
||||
#ifndef HAS_EXTENDED_TYPES
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#endif
|
||||
#if HAS_EXTENDED_TYPES
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
#endif
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
|
||||
#ifdef VULKAN
|
||||
|
||||
#ifndef HAS_EXTENDED_TYPES
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#endif
|
||||
#if HAS_EXTENDED_TYPES
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
#endif
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
|
||||
#ifdef VULKAN
|
||||
|
||||
#ifndef HAS_EXTENDED_TYPES
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#endif
|
||||
#if HAS_EXTENDED_TYPES
|
||||
#extension GL_EXT_shader_16bit_storage : require
|
||||
#extension GL_EXT_shader_8bit_storage : require
|
||||
#endif
|
||||
#define HAS_EXTENDED_TYPES 1
|
||||
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
|
||||
#define END_PUSH_CONSTANTS };
|
||||
#define UNIFORM(n)
|
||||
|
||||
@@ -7,7 +7,6 @@ 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;
|
||||
@@ -16,5 +15,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 = crop_offset + vec2(x, y) * abs(resize_factor) * 0.5;
|
||||
texcoord = vec2(x, y) * abs(resize_factor) * 0.5;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ 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,7 +13,6 @@
|
||||
layout( push_constant ) uniform constants {
|
||||
vec4 ViewportInfo[1];
|
||||
vec2 ResizeFactor;
|
||||
vec2 CropOffset;
|
||||
float EdgeSharpness;
|
||||
};
|
||||
layout(set = 0, binding = 0) uniform sampler2D ps0;
|
||||
|
||||
@@ -231,7 +231,6 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
.has_broken_unsigned_image_offsets = true,
|
||||
.has_broken_signed_operations = true,
|
||||
.has_broken_fp16_float_controls = false,
|
||||
.has_broken_fp32_denorm_flush = false,
|
||||
.has_gl_component_indexing_bug = device.HasComponentIndexingBug(),
|
||||
.has_gl_precise_bug = device.HasPreciseBug(),
|
||||
.has_gl_cbuf_ftou_bug = device.HasCbufFtouBug(),
|
||||
|
||||
@@ -370,9 +370,20 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
|
||||
const VkImageView null_image_view{texture_cache.GetImageView(VideoCommon::NULL_IMAGE_VIEW_ID).Handle(desc.type)};
|
||||
if (null_image_view != VK_NULL_HANDLE) vk_image_view = null_image_view;
|
||||
}
|
||||
Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||
guest_descriptor_queue.AddSampledImage(vk_image_view,
|
||||
sampler.HandleFor(image_view, desc.is_depth));
|
||||
const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
|
||||
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
|
||||
!image_view.SupportsAnisotropy()};
|
||||
VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
|
||||
: sampler.Handle()};
|
||||
if (sampler.HasLinearFiltering() &&
|
||||
VideoCore::Surface::IsPixelFormatInteger(image_view.format)) {
|
||||
vk_sampler = sampler.HandleWithNearestFilter();
|
||||
}
|
||||
if (desc.is_depth && sampler.HasDepthComparison() &&
|
||||
!image_view.SupportsDepthComparison()) {
|
||||
vk_sampler = sampler.HandleWithoutDepthComparison();
|
||||
}
|
||||
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
|
||||
const bool element_rescaled{texture_cache.IsRescaling(image_view)};
|
||||
is_rescaled |= element_rescaled;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// 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"
|
||||
@@ -19,7 +17,7 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
using PushConstants = std::array<u32, 4 + 2 + 2 + 1>;
|
||||
using PushConstants = std::array<u32, 4 + 2 + 1>;
|
||||
|
||||
SGSR::SGSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, VkExtent2D extent, bool edge_dir)
|
||||
: m_memory_allocator{memory_allocator}
|
||||
@@ -102,28 +100,26 @@ 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 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;
|
||||
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;
|
||||
|
||||
// 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>(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);
|
||||
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);
|
||||
|
||||
UploadImages(device, scheduler);
|
||||
UpdateDescriptorSets(device, source_image_view, image_index);
|
||||
|
||||
@@ -22,13 +22,7 @@
|
||||
#include "video_core/host_shaders/resolve_conditional_render_comp_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_quad_indexed_comp_spv.h"
|
||||
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_2d_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_2d_nonarrow_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_comp_spv.h"
|
||||
#include "video_core/host_shaders/block_linear_unswizzle_3d_nonarrow_comp_spv.h"
|
||||
#include "video_core/host_shaders/pitch_unswizzle_comp_spv.h"
|
||||
#include "video_core/host_shaders/pitch_unswizzle_nonarrow_comp_spv.h"
|
||||
#include "video_core/renderer_vulkan/vk_compute_pass.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
||||
@@ -878,291 +872,4 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
|
||||
});
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr u32 UNSWIZZLE_BINDING_INPUT_BUFFER = 0;
|
||||
constexpr u32 UNSWIZZLE_BINDING_OUTPUT_IMAGE = 1;
|
||||
constexpr size_t UNSWIZZLE_NUM_BINDINGS = 2;
|
||||
|
||||
constexpr std::array<VkDescriptorSetLayoutBinding, UNSWIZZLE_NUM_BINDINGS>
|
||||
UNSWIZZLE_DESCRIPTOR_SET_BINDINGS{{
|
||||
{
|
||||
.binding = UNSWIZZLE_BINDING_INPUT_BUFFER,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
{
|
||||
.binding = UNSWIZZLE_BINDING_OUTPUT_IMAGE,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
|
||||
.pImmutableSamplers = nullptr,
|
||||
},
|
||||
}};
|
||||
|
||||
constexpr std::array<VkDescriptorUpdateTemplateEntry, UNSWIZZLE_NUM_BINDINGS>
|
||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE{{
|
||||
{
|
||||
.dstBinding = UNSWIZZLE_BINDING_INPUT_BUFFER,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.offset = UNSWIZZLE_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
{
|
||||
.dstBinding = UNSWIZZLE_BINDING_OUTPUT_IMAGE,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.offset = UNSWIZZLE_BINDING_OUTPUT_IMAGE * sizeof(DescriptorUpdateEntry),
|
||||
.stride = sizeof(DescriptorUpdateEntry),
|
||||
},
|
||||
}};
|
||||
|
||||
constexpr DescriptorBankInfo UNSWIZZLE_BANK_INFO{
|
||||
.uniform_buffers = 0,
|
||||
.storage_buffers = 1,
|
||||
.texture_buffers = 0,
|
||||
.image_buffers = 0,
|
||||
.textures = 0,
|
||||
.images = 1,
|
||||
.score = 2,
|
||||
};
|
||||
|
||||
[[nodiscard]] std::span<const u32> UnswizzleSpv(const Device& device,
|
||||
std::span<const u32> extended,
|
||||
std::span<const u32> narrow) {
|
||||
if (device.IsStorageBuffer8BitAccessSupported() &&
|
||||
device.IsStorageBuffer16BitAccessSupported()) {
|
||||
return extended;
|
||||
}
|
||||
return narrow;
|
||||
}
|
||||
|
||||
struct PitchUnswizzlePushConstants {
|
||||
alignas(8) std::array<u32, 2> origin;
|
||||
alignas(8) std::array<s32, 2> destination;
|
||||
u32 bytes_per_block;
|
||||
u32 pitch;
|
||||
};
|
||||
|
||||
void RecordUnswizzleEntryBarrier(Scheduler& scheduler, VkPipeline vk_pipeline, VkImage vk_image,
|
||||
VkImageAspectFlags aspect_mask, bool is_initialized) {
|
||||
scheduler.Record([vk_pipeline, vk_image, aspect_mask,
|
||||
is_initialized](vk::CommandBuffer cmdbuf) {
|
||||
VkAccessFlags src_access = VK_ACCESS_NONE;
|
||||
VkImageLayout old_layout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
if (is_initialized) {
|
||||
src_access = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
old_layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
const VkImageMemoryBarrier image_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = src_access,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.oldLayout = old_layout,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = vk_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
VkPipelineStageFlags src_stage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
if (is_initialized) {
|
||||
src_stage = vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER;
|
||||
}
|
||||
cmdbuf.PipelineBarrier(src_stage, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, image_barrier);
|
||||
cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, vk_pipeline);
|
||||
});
|
||||
}
|
||||
|
||||
void RecordUnswizzleExitBarrier(Scheduler& scheduler, VkImage vk_image,
|
||||
VkImageAspectFlags aspect_mask) {
|
||||
scheduler.Record([vk_image, aspect_mask](vk::CommandBuffer cmdbuf) {
|
||||
const VkImageMemoryBarrier image_barrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.pNext = nullptr,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = vk_image,
|
||||
.subresourceRange{
|
||||
.aspectMask = aspect_mask,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = VK_REMAINING_MIP_LEVELS,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = VK_REMAINING_ARRAY_LAYERS,
|
||||
},
|
||||
};
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
|
||||
vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER, 0, image_barrier);
|
||||
});
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
BlockLinearUnswizzle2DPass::BlockLinearUnswizzle2DPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(
|
||||
VideoCommon::Accelerated::BlockLinearSwizzle2DParams)>,
|
||||
UnswizzleSpv(device_, BLOCK_LINEAR_UNSWIZZLE_2D_COMP_SPV,
|
||||
BLOCK_LINEAR_UNSWIZZLE_2D_NONARROW_COMP_SPV)),
|
||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||
|
||||
BlockLinearUnswizzle2DPass::~BlockLinearUnswizzle2DPass() = default;
|
||||
|
||||
void BlockLinearUnswizzle2DPass::Unswizzle(
|
||||
Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
using namespace VideoCommon::Accelerated;
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
const VkPipeline vk_pipeline = *pipeline;
|
||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||
const VkImage vk_image = image.Handle();
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||
|
||||
const u32 num_layers = static_cast<u32>(image.info.resources.layers);
|
||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
|
||||
const auto params = MakeBlockLinearSwizzle2DParams(swizzle, image.info);
|
||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_layers, params,
|
||||
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_layers);
|
||||
});
|
||||
}
|
||||
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
||||
}
|
||||
|
||||
BlockLinearUnswizzleImage3DPass::BlockLinearUnswizzleImage3DPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearSwizzle3DParams)>,
|
||||
UnswizzleSpv(device_, BLOCK_LINEAR_UNSWIZZLE_3D_COMP_SPV,
|
||||
BLOCK_LINEAR_UNSWIZZLE_3D_NONARROW_COMP_SPV)),
|
||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||
|
||||
BlockLinearUnswizzleImage3DPass::~BlockLinearUnswizzleImage3DPass() = default;
|
||||
|
||||
void BlockLinearUnswizzleImage3DPass::Unswizzle(
|
||||
Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
using namespace VideoCommon::Accelerated;
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
const VkPipeline vk_pipeline = *pipeline;
|
||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||
const VkImage vk_image = image.Handle();
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||
|
||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 16U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
|
||||
const u32 num_dispatches_z = Common::DivCeil(swizzle.num_tiles.depth, 8U);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
|
||||
const auto params = MakeBlockLinearSwizzle3DParams(swizzle, image.info);
|
||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, num_dispatches_z, params,
|
||||
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, num_dispatches_z);
|
||||
});
|
||||
}
|
||||
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
||||
}
|
||||
|
||||
PitchUnswizzlePass::PitchUnswizzlePass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
|
||||
: ComputePass(device_, scheduler_, descriptor_pool_, UNSWIZZLE_DESCRIPTOR_SET_BINDINGS,
|
||||
UNSWIZZLE_DESCRIPTOR_UPDATE_TEMPLATE, UNSWIZZLE_BANK_INFO,
|
||||
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(PitchUnswizzlePushConstants)>,
|
||||
UnswizzleSpv(device_, PITCH_UNSWIZZLE_COMP_SPV,
|
||||
PITCH_UNSWIZZLE_NONARROW_COMP_SPV)),
|
||||
scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {}
|
||||
|
||||
PitchUnswizzlePass::~PitchUnswizzlePass() = default;
|
||||
|
||||
void PitchUnswizzlePass::Unswizzle(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles) {
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
const VkPipeline vk_pipeline = *pipeline;
|
||||
const VkImageAspectFlags aspect_mask = image.AspectMask();
|
||||
const VkImage vk_image = image.Handle();
|
||||
const bool is_initialized = image.ExchangeInitialization();
|
||||
RecordUnswizzleEntryBarrier(scheduler, vk_pipeline, vk_image, aspect_mask, is_initialized);
|
||||
|
||||
const u32 bytes_per_block = VideoCore::Surface::BytesPerBlock(image.info.format);
|
||||
const u32 pitch = image.info.pitch;
|
||||
for (const VideoCommon::SwizzleParameters& swizzle : swizzles) {
|
||||
const size_t input_offset = swizzle.buffer_offset + map.offset;
|
||||
const u32 num_dispatches_x = Common::DivCeil(swizzle.num_tiles.width, 32U);
|
||||
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 32U);
|
||||
|
||||
compute_pass_descriptor_queue.Acquire(scheduler, 2);
|
||||
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
|
||||
image.guest_size_bytes - swizzle.buffer_offset);
|
||||
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
|
||||
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
|
||||
|
||||
const PitchUnswizzlePushConstants params{
|
||||
.origin{0, 0},
|
||||
.destination{0, 0},
|
||||
.bytes_per_block = bytes_per_block,
|
||||
.pitch = pitch,
|
||||
};
|
||||
scheduler.Record([this, num_dispatches_x, num_dispatches_y, params,
|
||||
descriptor_data](vk::CommandBuffer cmdbuf) {
|
||||
const VkDescriptorSet set = descriptor_allocator.Commit();
|
||||
device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data);
|
||||
cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {});
|
||||
cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, params);
|
||||
cmdbuf.Dispatch(num_dispatches_x, num_dispatches_y, 1);
|
||||
});
|
||||
}
|
||||
RecordUnswizzleExitBarrier(scheduler, vk_image, aspect_mask);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -164,49 +164,4 @@ private:
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
class BlockLinearUnswizzle2DPass final : public ComputePass {
|
||||
public:
|
||||
explicit BlockLinearUnswizzle2DPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||
~BlockLinearUnswizzle2DPass();
|
||||
|
||||
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
private:
|
||||
Scheduler& scheduler;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
class BlockLinearUnswizzleImage3DPass final : public ComputePass {
|
||||
public:
|
||||
explicit BlockLinearUnswizzleImage3DPass(
|
||||
const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||
~BlockLinearUnswizzleImage3DPass();
|
||||
|
||||
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
private:
|
||||
Scheduler& scheduler;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
class PitchUnswizzlePass final : public ComputePass {
|
||||
public:
|
||||
explicit PitchUnswizzlePass(const Device& device_, Scheduler& scheduler_,
|
||||
DescriptorPool& descriptor_pool_,
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue_);
|
||||
~PitchUnswizzlePass();
|
||||
|
||||
void Unswizzle(Image& image, const StagingBufferRef& map,
|
||||
std::span<const VideoCommon::SwizzleParameters> swizzles);
|
||||
|
||||
private:
|
||||
Scheduler& scheduler;
|
||||
ComputePassDescriptorQueue& compute_pass_descriptor_queue;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -444,7 +444,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
.has_broken_unsigned_image_offsets = false,
|
||||
.has_broken_signed_operations = false,
|
||||
.has_broken_fp16_float_controls = driver_id == VK_DRIVER_ID_NVIDIA_PROPRIETARY,
|
||||
.has_broken_fp32_denorm_flush = driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY,
|
||||
.ignore_nan_fp_comparisons = false,
|
||||
.has_broken_spirv_subgroup_mask_vector_extract_dynamic = false,
|
||||
.has_broken_robust =
|
||||
|
||||
@@ -449,9 +449,7 @@ void Scheduler::EndRenderPass()
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
|
||||
| VK_ACCESS_TRANSFER_READ_BIT
|
||||
| VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
@@ -462,7 +460,7 @@ void Scheduler::EndRenderPass()
|
||||
}
|
||||
cmdbuf.EndRenderPass();
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE_TRANSFER,
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE,
|
||||
0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
|
||||
if (has_transform_feedback) {
|
||||
static constexpr VkMemoryBarrier XFB_OUTPUT_BARRIER{
|
||||
|
||||
@@ -51,7 +51,6 @@ using VideoCore::Surface::BytesPerBlock;
|
||||
using VideoCore::Surface::HasAlpha;
|
||||
using VideoCore::Surface::IsPixelFormatASTC;
|
||||
using VideoCore::Surface::IsPixelFormatInteger;
|
||||
using VideoCore::Surface::IsPixelFormatSRGB;
|
||||
using VideoCore::Surface::SurfaceType;
|
||||
|
||||
namespace {
|
||||
@@ -60,17 +59,6 @@ constexpr bool ENABLE_MSAA_RESOLVE_CONSUME = true;
|
||||
constexpr bool ENABLE_MSAA_COLOR_DISCARD = true;
|
||||
constexpr bool ENABLE_MSAA_DEPTH_STENCIL_DISCARD = true;
|
||||
|
||||
[[nodiscard]] constexpr bool NeedsExplicitBorderColorFormat(VkFormat format) {
|
||||
switch (format) {
|
||||
case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
|
||||
case VK_FORMAT_B5G6R5_UNORM_PACK16:
|
||||
case VK_FORMAT_B5G5R5A1_UNORM_PACK16:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
if (color == std::array<float, 4>{0, 0, 0, 0}) {
|
||||
return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
|
||||
@@ -160,55 +148,6 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) {
|
||||
info.size.depth == 1;
|
||||
}
|
||||
|
||||
[[nodiscard]] PixelFormat UnswizzleViewFormat(u32 bytes_per_block) {
|
||||
switch (bytes_per_block) {
|
||||
case 1:
|
||||
return PixelFormat::R8_UINT;
|
||||
case 2:
|
||||
return PixelFormat::R16_UINT;
|
||||
case 4:
|
||||
return PixelFormat::R32_UINT;
|
||||
case 8:
|
||||
return PixelFormat::R32G32_UINT;
|
||||
case 16:
|
||||
return PixelFormat::R32G32B32A32_UINT;
|
||||
default:
|
||||
return PixelFormat::Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 UNSWIZZLE_WORKGROUP_INVOCATIONS = 32 * 32;
|
||||
|
||||
[[nodiscard]] bool SupportsAcceleratedUnswizzleDevice(const Device& device) {
|
||||
return device.IsKhrImageFormatListSupported() &&
|
||||
device.GetMaxComputeWorkGroupInvocations() >= UNSWIZZLE_WORKGROUP_INVOCATIONS;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool SupportsAcceleratedUnswizzle(const Device& device, const ImageInfo& info) {
|
||||
if (!SupportsAcceleratedUnswizzleDevice(device)) {
|
||||
return false;
|
||||
}
|
||||
if (info.num_samples > 1) {
|
||||
return false;
|
||||
}
|
||||
if (info.type != ImageType::e2D && info.type != ImageType::e3D &&
|
||||
info.type != ImageType::Linear) {
|
||||
return false;
|
||||
}
|
||||
const PixelFormat view_format =
|
||||
UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format));
|
||||
if (view_format == PixelFormat::Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (!VideoCore::Surface::IsViewCompatible(info.format, view_format, false, true)) {
|
||||
return false;
|
||||
}
|
||||
const auto host_format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, false, view_format);
|
||||
return device.IsFormatSupported(host_format.format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT,
|
||||
FormatType::Optimal);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageCreateInfo MakeImageCreateInfo(const Device& device, const ImageInfo& info,
|
||||
std::optional<VkFormat> format_override = {}) {
|
||||
auto format_info =
|
||||
@@ -297,18 +236,8 @@ constexpr u32 UNSWIZZLE_WORKGROUP_INVOCATIONS = 32 * 32;
|
||||
return allocator.CreateImage(image_ci);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageViewType StorageViewType(ImageType type) {
|
||||
if (type == ImageType::e3D) {
|
||||
return VK_IMAGE_VIEW_TYPE_3D;
|
||||
}
|
||||
if (type == ImageType::Linear) {
|
||||
return VK_IMAGE_VIEW_TYPE_2D;
|
||||
}
|
||||
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
||||
}
|
||||
|
||||
[[nodiscard]] vk::ImageView MakeStorageView(const vk::Device& device, u32 level, VkImage image,
|
||||
VkFormat format, VkImageViewType view_type) {
|
||||
VkFormat format) {
|
||||
static constexpr VkImageViewUsageCreateInfo storage_image_view_usage_create_info{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
@@ -319,7 +248,7 @@ constexpr u32 UNSWIZZLE_WORKGROUP_INVOCATIONS = 32 * 32;
|
||||
.pNext = &storage_image_view_usage_create_info,
|
||||
.flags = 0,
|
||||
.image = image,
|
||||
.viewType = view_type,
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY,
|
||||
.format = format,
|
||||
.components{
|
||||
.r = VK_COMPONENT_SWIZZLE_IDENTITY,
|
||||
@@ -717,11 +646,18 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
|
||||
.subresourceRange = subresource_range,
|
||||
};
|
||||
|
||||
cmdbuf.PipelineBarrier(vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
read_barrier);
|
||||
cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies);
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, vk::PIPELINE_STAGE_GRAPHICS_COMPUTE, 0,
|
||||
nullptr, nullptr, write_barrier);
|
||||
// TODO: Move this to another API
|
||||
cmdbuf.PipelineBarrier(
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
||||
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
0, nullptr, nullptr, write_barrier);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkImageBlit MakeImageBlit(const Region2D& dst_region, const Region2D& src_region,
|
||||
@@ -1020,14 +956,6 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, Scheduler& sched
|
||||
bl3d_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||
staging_buffer_pool, compute_pass_descriptor_queue);
|
||||
}
|
||||
if (SupportsAcceleratedUnswizzleDevice(device)) {
|
||||
bl_unswizzle_2d_pass.emplace(device, scheduler, descriptor_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
bl_unswizzle_image_3d_pass.emplace(device, scheduler, descriptor_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
pitch_unswizzle_pass.emplace(device, scheduler, descriptor_pool,
|
||||
compute_pass_descriptor_queue);
|
||||
}
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::Finish() {
|
||||
@@ -1874,6 +1802,10 @@ bool TextureCacheRuntime::CanReportMemoryUsage() const {
|
||||
return device.CanReportMemoryUsage();
|
||||
}
|
||||
|
||||
std::optional<size_t> TextureCacheRuntime::GetSamplerHeapBudget() const {
|
||||
return device.GetSamplerHeapBudget();
|
||||
}
|
||||
|
||||
void TextureCacheRuntime::FlushDeferredClear() {
|
||||
scheduler.FlushDeferredClear();
|
||||
}
|
||||
@@ -1962,12 +1894,16 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
|
||||
if (runtime->device.HasDebuggingToolAttached()) {
|
||||
original_image.SetObjectNameEXT(VideoCommon::Name(*this).c_str());
|
||||
}
|
||||
if (False(flags & VideoCommon::ImageFlagBits::Converted) &&
|
||||
SupportsAcceleratedUnswizzle(runtime->device, info)) {
|
||||
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
|
||||
}
|
||||
current_image = &Image::original_image;
|
||||
storage_image_views.resize(info.resources.levels);
|
||||
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
||||
const auto& device = runtime->device.GetLogical();
|
||||
const VkFormat storage_format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
for (s32 level = 0; level < info.resources.levels; ++level) {
|
||||
storage_image_views[level] =
|
||||
MakeStorageView(device, level, *original_image, storage_format);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
|
||||
@@ -2377,39 +2313,16 @@ void Image::DownloadMemory(const StagingBufferRef& map, std::span<const BufferIm
|
||||
DownloadMemory(buffers, offsets, copies);
|
||||
}
|
||||
|
||||
std::vector<vk::ImageView>& Image::StorageViewsFor(vk::Image Image::*image) {
|
||||
if (image == &Image::scaled_image) {
|
||||
if (scaled_storage_image_views.empty()) {
|
||||
scaled_storage_image_views.resize(info.resources.levels);
|
||||
}
|
||||
return scaled_storage_image_views;
|
||||
}
|
||||
return storage_image_views;
|
||||
}
|
||||
|
||||
VkImageView Image::StorageImageView(s32 level) noexcept {
|
||||
const bool astc_decode = WillUseAcceleratedAstcDecode(runtime->device, info);
|
||||
const bool unswizzle_upload =
|
||||
!astc_decode && True(flags & ImageFlagBits::AcceleratedUpload);
|
||||
vk::Image Image::*target = current_image;
|
||||
if (astc_decode || unswizzle_upload) {
|
||||
target = &Image::original_image;
|
||||
}
|
||||
auto& view = StorageViewsFor(target)[level];
|
||||
auto& view = storage_image_views[level];
|
||||
if (!view) {
|
||||
auto format_info =
|
||||
MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, true, info.format);
|
||||
if (astc_decode) {
|
||||
if (WillUseAcceleratedAstcDecode(runtime->device, info)) {
|
||||
format_info.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
|
||||
}
|
||||
if (unswizzle_upload) {
|
||||
const PixelFormat view_format =
|
||||
UnswizzleViewFormat(VideoCore::Surface::BytesPerBlock(info.format));
|
||||
format_info = MaxwellToVK::SurfaceFormat(runtime->device, FormatType::Optimal, false,
|
||||
view_format);
|
||||
}
|
||||
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*target),
|
||||
format_info.format, StorageViewType(info.type));
|
||||
view = MakeStorageView(runtime->device.GetLogical(), level, *(this->*current_image),
|
||||
format_info.format);
|
||||
}
|
||||
return *view;
|
||||
}
|
||||
@@ -2440,7 +2353,6 @@ bool Image::ScaleUp(bool ignore) {
|
||||
runtime->ViewFormats(info.format));
|
||||
ignore = false;
|
||||
}
|
||||
ignore = true;
|
||||
current_image = &Image::scaled_image;
|
||||
if (ignore) {
|
||||
return true;
|
||||
@@ -2469,7 +2381,6 @@ bool Image::ScaleDown(bool ignore) {
|
||||
}
|
||||
ASSERT(info.type != ImageType::Linear);
|
||||
flags &= ~ImageFlagBits::Rescaled;
|
||||
ignore = true;
|
||||
current_image = &Image::original_image;
|
||||
if (ignore) {
|
||||
return true;
|
||||
@@ -2606,23 +2517,9 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
supports_depth_comparison =
|
||||
(properties3.optimalTilingFeatures &
|
||||
VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_DEPTH_COMPARISON_BIT) != 0;
|
||||
supports_minmax_filter = (properties3.optimalTilingFeatures &
|
||||
VK_FORMAT_FEATURE_2_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0;
|
||||
} else {
|
||||
supports_depth_comparison = true;
|
||||
supports_minmax_filter =
|
||||
(device->GetPhysical().GetFormatProperties(format_info.format).optimalTilingFeatures &
|
||||
VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT) != 0;
|
||||
}
|
||||
requires_border_color_format = NeedsExplicitBorderColorFormat(format_info.format);
|
||||
swizzle_mapping = VkComponentMapping{
|
||||
.r = ComponentSwizzle(swizzle[0]),
|
||||
.g = ComponentSwizzle(swizzle[1]),
|
||||
.b = ComponentSwizzle(swizzle[2]),
|
||||
.a = ComponentSwizzle(swizzle[3]),
|
||||
};
|
||||
has_identity_swizzle = swizzle[0] == SwizzleSource::R && swizzle[1] == SwizzleSource::G &&
|
||||
swizzle[2] == SwizzleSource::B && swizzle[3] == SwizzleSource::A;
|
||||
const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format);
|
||||
const VkImageUsageFlags image_usage = image.UsageFlags();
|
||||
const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage;
|
||||
@@ -2647,7 +2544,12 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
.image = image.Handle(),
|
||||
.viewType = VkImageViewType{},
|
||||
.format = format_info.format,
|
||||
.components = swizzle_mapping,
|
||||
.components{
|
||||
.r = ComponentSwizzle(swizzle[0]),
|
||||
.g = ComponentSwizzle(swizzle[1]),
|
||||
.b = ComponentSwizzle(swizzle[2]),
|
||||
.a = ComponentSwizzle(swizzle[3]),
|
||||
},
|
||||
.subresourceRange = MakeSubresourceRange(aspect_mask, info.range),
|
||||
};
|
||||
const auto create = [&](TextureType tex_type, std::optional<u32> num_layers) {
|
||||
@@ -2816,223 +2718,92 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_
|
||||
});
|
||||
}
|
||||
|
||||
CustomBorderColorBudget::~CustomBorderColorBudget() {
|
||||
Release();
|
||||
}
|
||||
|
||||
CustomBorderColorBudget::CustomBorderColorBudget(CustomBorderColorBudget&& rhs) noexcept
|
||||
: device_ptr{std::exchange(rhs.device_ptr, nullptr)}, held{std::exchange(rhs.held, 0)} {}
|
||||
|
||||
CustomBorderColorBudget& CustomBorderColorBudget::operator=(
|
||||
CustomBorderColorBudget&& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
Release();
|
||||
device_ptr = std::exchange(rhs.device_ptr, nullptr);
|
||||
held = std::exchange(rhs.held, 0);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CustomBorderColorBudget::TryAcquire(const Device& device, size_t count) {
|
||||
if (!device.TryReserveCustomBorderColorSamplers(count)) {
|
||||
return false;
|
||||
}
|
||||
device_ptr = &device;
|
||||
held += count;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CustomBorderColorBudget::Release() noexcept {
|
||||
if (device_ptr != nullptr) {
|
||||
device_ptr->ReleaseCustomBorderColorSamplers(held);
|
||||
}
|
||||
device_ptr = nullptr;
|
||||
held = 0;
|
||||
}
|
||||
|
||||
Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) {
|
||||
const auto& device = runtime.device;
|
||||
device_ptr = &device;
|
||||
border_color = tsc.BorderColor();
|
||||
srgb_border_color = tsc.SrgbBorderColor();
|
||||
const bool has_custom_border_extension = runtime.device.IsExtCustomBorderColorSupported();
|
||||
const bool has_format_undefined =
|
||||
has_custom_border_extension && runtime.device.IsCustomBorderColorWithoutFormatSupported();
|
||||
const bool has_custom_border_colors =
|
||||
has_format_undefined && runtime.device.IsCustomBorderColorsSupported();
|
||||
const auto color = tsc.BorderColor();
|
||||
|
||||
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
||||
default_anisotropy = static_cast<f32>(1U << tsc.max_anisotropy);
|
||||
|
||||
const VkFilter mag_filter{MaxwellToVK::Sampler::Filter(tsc.mag_filter)};
|
||||
const VkFilter min_filter{MaxwellToVK::Sampler::Filter(tsc.min_filter)};
|
||||
const VkSamplerMipmapMode mipmap_mode{MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter)};
|
||||
|
||||
const VkSamplerAddressMode wrap_u{
|
||||
MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter)};
|
||||
const VkSamplerAddressMode wrap_v{
|
||||
MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter)};
|
||||
const VkSamplerAddressMode wrap_p{
|
||||
MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter)};
|
||||
const bool samples_border = wrap_u == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
|
||||
wrap_v == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
|
||||
wrap_p == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
|
||||
reduction_mode = MaxwellToVK::SamplerReduction(tsc.reduction_filter);
|
||||
|
||||
has_added_anisotropy = max_anisotropy > default_anisotropy;
|
||||
has_linear_filtering = mag_filter == VK_FILTER_LINEAR || min_filter == VK_FILTER_LINEAR ||
|
||||
mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
has_depth_comparison = tsc.depth_compare_enabled != 0;
|
||||
has_minmax_reduction = reduction_mode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT;
|
||||
has_srgb_border_color = tsc.srgb_conversion != 0 && srgb_border_color != border_color;
|
||||
if (has_minmax_reduction && !device.IsExtSamplerFilterMinmaxSupported()) {
|
||||
LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required");
|
||||
has_minmax_reduction = false;
|
||||
}
|
||||
has_custom_border_colors = samples_border && device.IsCustomBorderColorUsable();
|
||||
needs_swizzle_mapping = has_custom_border_colors && device.NeedsBorderColorSwizzleMapping();
|
||||
|
||||
if (has_custom_border_colors && GPU::Logging::IsActive()) {
|
||||
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
||||
"VK_EXT_custom_border_color", "Sampler::Sampler");
|
||||
}
|
||||
if (device.IsExtBorderColorSwizzleSupported() && GPU::Logging::IsActive()) {
|
||||
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
||||
"VK_EXT_border_color_swizzle", "Sampler::Sampler");
|
||||
}
|
||||
|
||||
f32 min_lod = 0.0f;
|
||||
f32 max_lod = 0.25f;
|
||||
if (tsc.mipmap_filter != TextureMipmapFilter::None) {
|
||||
min_lod = tsc.MinLod();
|
||||
max_lod = tsc.MaxLod();
|
||||
}
|
||||
base_ci = VkSamplerCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.magFilter = mag_filter,
|
||||
.minFilter = min_filter,
|
||||
.mipmapMode = mipmap_mode,
|
||||
.addressModeU = wrap_u,
|
||||
.addressModeV = wrap_v,
|
||||
.addressModeW = wrap_p,
|
||||
.mipLodBias = tsc.LodBias(),
|
||||
.anisotropyEnable = static_cast<VkBool32>(max_anisotropy > 1.0f),
|
||||
.maxAnisotropy = max_anisotropy,
|
||||
.compareEnable = static_cast<VkBool32>(tsc.depth_compare_enabled),
|
||||
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
||||
.minLod = min_lod,
|
||||
.maxLod = max_lod,
|
||||
.borderColor = VK_BORDER_COLOR_FLOAT_CUSTOM_EXT,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
variants.reserve(1);
|
||||
Emplace(VariantKey{});
|
||||
}
|
||||
|
||||
Sampler::VariantKey Sampler::MakeKey(const ImageView& image_view, bool is_depth) const noexcept {
|
||||
VariantKey key{};
|
||||
key.reduce_anisotropy = has_added_anisotropy && !image_view.SupportsAnisotropy();
|
||||
key.force_nearest = has_linear_filtering && IsPixelFormatInteger(image_view.format);
|
||||
key.drop_depth_comparison =
|
||||
is_depth && has_depth_comparison && !image_view.SupportsDepthComparison();
|
||||
key.drop_reduction = has_minmax_reduction && !image_view.SupportsMinmaxFilter();
|
||||
key.drop_custom_border = has_custom_border_colors && image_view.RequiresBorderColorFormat();
|
||||
key.srgb_border = has_srgb_border_color && IsPixelFormatSRGB(image_view.format);
|
||||
if (needs_swizzle_mapping && !key.drop_custom_border && !image_view.HasIdentitySwizzle()) {
|
||||
const VkComponentMapping& mapping = image_view.Swizzle();
|
||||
key.swizzle = {mapping.r, mapping.g, mapping.b, mapping.a};
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
VkSampler Sampler::Find(const VariantKey& key) const noexcept {
|
||||
const auto it = std::ranges::find(variants, key, &Variant::key);
|
||||
if (it == variants.end()) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
return *it->sampler;
|
||||
}
|
||||
|
||||
VkSampler Sampler::Emplace(VariantKey key) {
|
||||
bool custom_border = has_custom_border_colors && !key.drop_custom_border;
|
||||
if (custom_border && !custom_border_color_budget.TryAcquire(*device_ptr, 1)) {
|
||||
static bool warned_budget = false;
|
||||
if (!warned_budget) {
|
||||
warned_budget = true;
|
||||
}
|
||||
custom_border = false;
|
||||
key.drop_custom_border = true;
|
||||
key.swizzle = {};
|
||||
if (const VkSampler existing = Find(key); existing != VK_NULL_HANDLE) {
|
||||
return existing;
|
||||
}
|
||||
}
|
||||
|
||||
std::array<float, 4> color = border_color;
|
||||
if (key.srgb_border) {
|
||||
color = srgb_border_color;
|
||||
}
|
||||
const VkSamplerCustomBorderColorCreateInfoEXT border_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
|
||||
.pNext = nullptr,
|
||||
.customBorderColor = std::bit_cast<VkClearColorValue>(color),
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
};
|
||||
const VkSamplerBorderColorComponentMappingCreateInfoEXT mapping_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT,
|
||||
.pNext = &border_ci,
|
||||
.components{key.swizzle[0], key.swizzle[1], key.swizzle[2], key.swizzle[3]},
|
||||
.srgb = VK_FALSE,
|
||||
};
|
||||
const void* chain = nullptr;
|
||||
if (custom_border) {
|
||||
chain = &border_ci;
|
||||
if (key.HasSwizzle()) {
|
||||
chain = &mapping_ci;
|
||||
const void* pnext = nullptr;
|
||||
if (has_custom_border_colors) {
|
||||
pnext = &border_ci;
|
||||
if (GPU::Logging::IsActive()) {
|
||||
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
||||
"VK_EXT_custom_border_color", "Sampler::Sampler");
|
||||
}
|
||||
}
|
||||
if (device.IsExtBorderColorSwizzleSupported() && GPU::Logging::IsActive()) {
|
||||
GPU::Logging::GPULogger::GetInstance().LogExtensionUsage(
|
||||
"VK_EXT_border_color_swizzle", "Sampler::Sampler");
|
||||
}
|
||||
const VkSamplerReductionModeCreateInfoEXT reduction_ci{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT,
|
||||
.pNext = chain,
|
||||
.reductionMode = reduction_mode,
|
||||
.pNext = pnext,
|
||||
.reductionMode = MaxwellToVK::SamplerReduction(tsc.reduction_filter),
|
||||
};
|
||||
if (has_minmax_reduction && !key.drop_reduction) {
|
||||
chain = &reduction_ci;
|
||||
if (runtime.device.IsExtSamplerFilterMinmaxSupported()) {
|
||||
pnext = &reduction_ci;
|
||||
} else if (reduction_ci.reductionMode != VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT) {
|
||||
LOG_WARNING(Render_Vulkan, "VK_EXT_sampler_filter_minmax is required");
|
||||
}
|
||||
// Some games have samplers with garbage. Sanitize them here.
|
||||
const f32 max_anisotropy = std::clamp(tsc.MaxAnisotropy(), 1.0f, 16.0f);
|
||||
|
||||
VkSamplerCreateInfo create_info = base_ci;
|
||||
create_info.pNext = chain;
|
||||
if (key.force_nearest) {
|
||||
create_info.magFilter = VK_FILTER_NEAREST;
|
||||
create_info.minFilter = VK_FILTER_NEAREST;
|
||||
create_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
create_info.anisotropyEnable = VK_FALSE;
|
||||
create_info.maxAnisotropy = 1.0f;
|
||||
} else if (key.reduce_anisotropy) {
|
||||
create_info.anisotropyEnable = static_cast<VkBool32>(default_anisotropy > 1.0f);
|
||||
create_info.maxAnisotropy = default_anisotropy;
|
||||
}
|
||||
if (key.drop_depth_comparison) {
|
||||
create_info.compareEnable = VK_FALSE;
|
||||
}
|
||||
if (!custom_border) {
|
||||
create_info.borderColor = ConvertBorderColor(color);
|
||||
}
|
||||
variants.push_back(Variant{
|
||||
.key = key,
|
||||
.sampler = device_ptr->GetLogical().CreateSampler(create_info),
|
||||
});
|
||||
return *variants.back().sampler;
|
||||
}
|
||||
const VkFilter mag_filter{MaxwellToVK::Sampler::Filter(tsc.mag_filter)};
|
||||
const VkFilter min_filter{MaxwellToVK::Sampler::Filter(tsc.min_filter)};
|
||||
const VkSamplerMipmapMode mipmap_mode{MaxwellToVK::Sampler::MipmapMode(tsc.mipmap_filter)};
|
||||
const bool has_linear_filtering{mag_filter == VK_FILTER_LINEAR ||
|
||||
min_filter == VK_FILTER_LINEAR ||
|
||||
mipmap_mode == VK_SAMPLER_MIPMAP_MODE_LINEAR};
|
||||
|
||||
VkSampler Sampler::HandleFor(const ImageView& image_view, bool is_depth) {
|
||||
VariantKey key = MakeKey(image_view, is_depth);
|
||||
if (variants.size() >= MAX_VARIANTS) {
|
||||
key.srgb_border = false;
|
||||
key.swizzle = {};
|
||||
const auto create_sampler = [&](const f32 anisotropy, bool force_nearest,
|
||||
bool disable_compare = false) {
|
||||
return device.GetLogical().CreateSampler(VkSamplerCreateInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = pnext,
|
||||
.flags = 0,
|
||||
.magFilter = force_nearest ? VK_FILTER_NEAREST : mag_filter,
|
||||
.minFilter = force_nearest ? VK_FILTER_NEAREST : min_filter,
|
||||
.mipmapMode = force_nearest ? VK_SAMPLER_MIPMAP_MODE_NEAREST : mipmap_mode,
|
||||
.addressModeU = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_u, tsc.mag_filter),
|
||||
.addressModeV = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_v, tsc.mag_filter),
|
||||
.addressModeW = MaxwellToVK::Sampler::WrapMode(device, tsc.wrap_p, tsc.mag_filter),
|
||||
.mipLodBias = tsc.LodBias(),
|
||||
.anisotropyEnable =
|
||||
static_cast<VkBool32>(!force_nearest && anisotropy > 1.0f ? VK_TRUE : VK_FALSE),
|
||||
.maxAnisotropy = force_nearest ? 1.0f : anisotropy,
|
||||
.compareEnable = disable_compare ? VK_FALSE
|
||||
: static_cast<VkBool32>(tsc.depth_compare_enabled),
|
||||
.compareOp = MaxwellToVK::Sampler::DepthCompareFunction(tsc.depth_compare_func),
|
||||
.minLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.0f : tsc.MinLod(),
|
||||
.maxLod = tsc.mipmap_filter == TextureMipmapFilter::None ? 0.25f : tsc.MaxLod(),
|
||||
.borderColor = has_custom_border_colors ? VK_BORDER_COLOR_FLOAT_CUSTOM_EXT
|
||||
: ConvertBorderColor(color),
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
});
|
||||
};
|
||||
|
||||
sampler = create_sampler(max_anisotropy, false);
|
||||
|
||||
const f32 max_anisotropy_default = static_cast<f32>(1U << tsc.max_anisotropy);
|
||||
if (max_anisotropy > max_anisotropy_default) {
|
||||
sampler_default_anisotropy = create_sampler(max_anisotropy_default, false);
|
||||
}
|
||||
if (const VkSampler existing = Find(key); existing != VK_NULL_HANDLE) {
|
||||
return existing;
|
||||
if (has_linear_filtering) {
|
||||
sampler_nearest = create_sampler(1.0f, true);
|
||||
}
|
||||
if (tsc.depth_compare_enabled) {
|
||||
sampler_noncompare = create_sampler(max_anisotropy, false, true);
|
||||
}
|
||||
return Emplace(key);
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(TextureCacheRuntime& runtime, std::span<ImageView*, NUM_RT> color_buffers,
|
||||
@@ -3222,19 +2993,6 @@ void TextureCacheRuntime::AccelerateImageUpload(
|
||||
return astc_decoder_pass->Assemble(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (bl_unswizzle_2d_pass && image.info.type == ImageType::e2D) {
|
||||
return bl_unswizzle_2d_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (bl_unswizzle_image_3d_pass && image.info.type == ImageType::e3D &&
|
||||
!IsPixelFormatBCn(image.info.format)) {
|
||||
return bl_unswizzle_image_3d_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (pitch_unswizzle_pass && image.info.type == ImageType::Linear) {
|
||||
return pitch_unswizzle_pass->Unswizzle(image, map, swizzles);
|
||||
}
|
||||
|
||||
if (!Settings::values.gpu_unswizzle_enabled.GetValue() || !bl3d_unswizzle_pass) {
|
||||
if (IsPixelFormatBCn(image.info.format) && image.info.type == ImageType::e3D) {
|
||||
ASSERT(false && "GPU unswizzle is disabled for BCn 3D texture");
|
||||
|
||||
@@ -68,6 +68,8 @@ public:
|
||||
|
||||
bool CanReportMemoryUsage() const;
|
||||
|
||||
std::optional<size_t> GetSamplerHeapBudget() const;
|
||||
|
||||
bool CanDownloadMsaa(const VideoCommon::ImageInfo& info) const;
|
||||
|
||||
[[nodiscard]] VkImage AcquireMsaaScratchImage(const VkImageCreateInfo& image_ci);
|
||||
@@ -159,9 +161,6 @@ public:
|
||||
std::optional<ASTCDecoderPass> astc_decoder_pass;
|
||||
|
||||
std::optional<BlockLinearUnswizzle3DPass> bl3d_unswizzle_pass;
|
||||
std::optional<BlockLinearUnswizzle2DPass> bl_unswizzle_2d_pass;
|
||||
std::optional<BlockLinearUnswizzleImage3DPass> bl_unswizzle_image_3d_pass;
|
||||
std::optional<PitchUnswizzlePass> pitch_unswizzle_pass;
|
||||
const Settings::ResolutionScalingInfo& resolution;
|
||||
std::array<std::vector<VkFormat>, VideoCore::Surface::MaxPixelFormat> view_formats;
|
||||
|
||||
@@ -373,8 +372,6 @@ private:
|
||||
|
||||
bool NeedsScaleHelper() const;
|
||||
|
||||
std::vector<vk::ImageView>& StorageViewsFor(vk::Image Image::*image);
|
||||
|
||||
Scheduler* scheduler{};
|
||||
TextureCacheRuntime* runtime{};
|
||||
|
||||
@@ -392,7 +389,6 @@ private:
|
||||
vk::Image Image::*current_image{};
|
||||
|
||||
std::vector<vk::ImageView> storage_image_views;
|
||||
std::vector<vk::ImageView> scaled_storage_image_views;
|
||||
VkImageAspectFlags aspect_mask = 0;
|
||||
bool initialized = false;
|
||||
|
||||
@@ -450,22 +446,6 @@ public:
|
||||
return supports_depth_comparison;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool RequiresBorderColorFormat() const noexcept {
|
||||
return requires_border_color_format;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool SupportsMinmaxFilter() const noexcept {
|
||||
return supports_minmax_filter;
|
||||
}
|
||||
|
||||
[[nodiscard]] const VkComponentMapping& Swizzle() const noexcept {
|
||||
return swizzle_mapping;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasIdentitySwizzle() const noexcept {
|
||||
return has_identity_swizzle;
|
||||
}
|
||||
|
||||
[[nodiscard]] GPUVAddr GpuAddr() const noexcept {
|
||||
return gpu_addr;
|
||||
}
|
||||
@@ -498,91 +478,48 @@ private:
|
||||
VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
u32 buffer_size = 0;
|
||||
|
||||
VkComponentMapping swizzle_mapping{};
|
||||
|
||||
bool supports_depth_comparison = false;
|
||||
bool requires_border_color_format = false;
|
||||
bool supports_minmax_filter = false;
|
||||
bool has_identity_swizzle = true;
|
||||
};
|
||||
|
||||
class ImageAlloc : public VideoCommon::ImageAllocBase {};
|
||||
|
||||
class CustomBorderColorBudget {
|
||||
public:
|
||||
CustomBorderColorBudget() = default;
|
||||
~CustomBorderColorBudget();
|
||||
|
||||
CustomBorderColorBudget(const CustomBorderColorBudget&) = delete;
|
||||
CustomBorderColorBudget& operator=(const CustomBorderColorBudget&) = delete;
|
||||
|
||||
CustomBorderColorBudget(CustomBorderColorBudget&& rhs) noexcept;
|
||||
CustomBorderColorBudget& operator=(CustomBorderColorBudget&& rhs) noexcept;
|
||||
|
||||
bool TryAcquire(const Device& device, size_t count);
|
||||
|
||||
private:
|
||||
void Release() noexcept;
|
||||
|
||||
const Device* device_ptr = nullptr;
|
||||
size_t held = 0;
|
||||
};
|
||||
|
||||
class Sampler {
|
||||
public:
|
||||
explicit Sampler(TextureCacheRuntime&, const Tegra::Texture::TSCEntry&);
|
||||
|
||||
[[nodiscard]] VkSampler Handle() const noexcept {
|
||||
return *variants.front().sampler;
|
||||
return *sampler;
|
||||
}
|
||||
|
||||
[[nodiscard]] VkSampler HandleFor(const ImageView& image_view, bool is_depth);
|
||||
[[nodiscard]] VkSampler HandleWithDefaultAnisotropy() const noexcept {
|
||||
return *sampler_default_anisotropy;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasAddedAnisotropy() const noexcept {
|
||||
return static_cast<bool>(sampler_default_anisotropy);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkSampler HandleWithNearestFilter() const noexcept {
|
||||
return *sampler_nearest;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasLinearFiltering() const noexcept {
|
||||
return static_cast<bool>(sampler_nearest);
|
||||
}
|
||||
|
||||
[[nodiscard]] VkSampler HandleWithoutDepthComparison() const noexcept {
|
||||
return *sampler_noncompare;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool HasDepthComparison() const noexcept {
|
||||
return static_cast<bool>(sampler_noncompare);
|
||||
}
|
||||
|
||||
private:
|
||||
struct VariantKey {
|
||||
bool reduce_anisotropy;
|
||||
bool force_nearest;
|
||||
bool drop_depth_comparison;
|
||||
bool drop_reduction;
|
||||
bool drop_custom_border;
|
||||
bool srgb_border;
|
||||
std::array<VkComponentSwizzle, 4> swizzle;
|
||||
|
||||
bool operator==(const VariantKey&) const noexcept = default;
|
||||
|
||||
[[nodiscard]] bool HasSwizzle() const noexcept {
|
||||
return swizzle != std::array<VkComponentSwizzle, 4>{};
|
||||
}
|
||||
};
|
||||
|
||||
struct Variant {
|
||||
VariantKey key;
|
||||
vk::Sampler sampler;
|
||||
};
|
||||
|
||||
static constexpr size_t MAX_VARIANTS = 32;
|
||||
|
||||
[[nodiscard]] VariantKey MakeKey(const ImageView& image_view, bool is_depth) const noexcept;
|
||||
[[nodiscard]] VkSampler Find(const VariantKey& key) const noexcept;
|
||||
VkSampler Emplace(VariantKey key);
|
||||
|
||||
CustomBorderColorBudget custom_border_color_budget;
|
||||
std::vector<Variant> variants;
|
||||
|
||||
const Device* device_ptr{nullptr};
|
||||
VkSamplerCreateInfo base_ci{};
|
||||
VkSamplerReductionModeEXT reduction_mode{VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT};
|
||||
std::array<float, 4> border_color{};
|
||||
std::array<float, 4> srgb_border_color{};
|
||||
f32 default_anisotropy{1.0f};
|
||||
|
||||
bool has_added_anisotropy{};
|
||||
bool has_linear_filtering{};
|
||||
bool has_depth_comparison{};
|
||||
bool has_minmax_reduction{};
|
||||
bool has_custom_border_colors{};
|
||||
bool has_srgb_border_color{};
|
||||
bool needs_swizzle_mapping{};
|
||||
vk::Sampler sampler;
|
||||
vk::Sampler sampler_default_anisotropy;
|
||||
vk::Sampler sampler_nearest;
|
||||
vk::Sampler sampler_noncompare;
|
||||
};
|
||||
|
||||
struct TextureCacheParams {
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -26,8 +23,8 @@ struct BlockLinearSwizzle2DParams {
|
||||
};
|
||||
|
||||
struct BlockLinearSwizzle3DParams {
|
||||
alignas(16) std::array<u32, 3> origin;
|
||||
alignas(16) std::array<s32, 3> destination;
|
||||
std::array<u32, 3> origin;
|
||||
std::array<s32, 3> destination;
|
||||
u32 bytes_per_block_log2;
|
||||
u32 slice_size;
|
||||
u32 block_size;
|
||||
|
||||
@@ -279,11 +279,11 @@ void TextureCache<P>::CheckFeedbackLoop(std::span<const ImageViewInOut> views) {
|
||||
|
||||
const ImageId view_image_id = slot_image_views[view.id].image_id;
|
||||
{
|
||||
bool is_feedback = false;
|
||||
bool is_continue = false;
|
||||
for (size_t i = 0; i < 8; ++i)
|
||||
is_feedback |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i];
|
||||
if (is_feedback)
|
||||
return true;
|
||||
is_continue |= (rt_active_mask & (1u << i)) && view_image_id == rt_image_id[i];
|
||||
if (is_continue)
|
||||
continue;
|
||||
}
|
||||
if (depth_active && view_image_id == rt_depth_image_id) {
|
||||
return true;
|
||||
@@ -1896,10 +1896,67 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config, bool compute) {
|
||||
const auto [pair, is_new] = channel_state->samplers.try_emplace(config);
|
||||
if (is_new) {
|
||||
pair->second = slot_samplers.insert(runtime, config);
|
||||
EnforceSamplerBudget();
|
||||
}
|
||||
return pair->second;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
std::optional<size_t> TextureCache<P>::QuerySamplerBudget() const {
|
||||
if constexpr (requires { runtime.GetSamplerHeapBudget(); }) {
|
||||
return runtime.GetSamplerHeapBudget();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::EnforceSamplerBudget() {
|
||||
if (auto const budget = QuerySamplerBudget(); budget) {
|
||||
if (slot_samplers.size() < *budget) {
|
||||
return;
|
||||
}
|
||||
if (!channel_state) {
|
||||
return;
|
||||
}
|
||||
if (last_sampler_gc_frame == frame_tick) {
|
||||
return;
|
||||
}
|
||||
last_sampler_gc_frame = frame_tick;
|
||||
TrimInactiveSamplers(*budget);
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void TextureCache<P>::TrimInactiveSamplers(size_t budget) {
|
||||
if (channel_state->samplers.size() > 0) {
|
||||
constexpr size_t SAMPLER_GC_SLACK = 1024;
|
||||
ankerl::unordered_dense::set<SamplerId> active_sampler_ids;
|
||||
for (auto const& e : channel_state->sampler_ids)
|
||||
active_sampler_ids.insert(e.second);
|
||||
// Elements in the map must be necesarily valid
|
||||
size_t removed = 0;
|
||||
for (auto it = channel_state->samplers.begin(); it != channel_state->samplers.end();) {
|
||||
const SamplerId sampler_id = it->second;
|
||||
if (!sampler_id || sampler_id == CORRUPT_ID) {
|
||||
it = channel_state->samplers.erase(it);
|
||||
} else if (std::ranges::find(active_sampler_ids, sampler_id) != active_sampler_ids.end()) {
|
||||
++it;
|
||||
} else {
|
||||
slot_samplers.erase(sampler_id);
|
||||
it = channel_state->samplers.erase(it);
|
||||
++removed;
|
||||
if (slot_samplers.size() + SAMPLER_GC_SLACK <= budget) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed != 0) {
|
||||
LOG_WARNING(HW_GPU, "Sampler cache exceeded {} entries on this driver; reclaimed {} inactive samplers", budget, removed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class P>
|
||||
ImageViewId TextureCache<P>::FindColorBuffer(size_t index) {
|
||||
const auto& regs = maxwell3d->regs;
|
||||
|
||||
@@ -420,6 +420,9 @@ private:
|
||||
|
||||
void QueueAsyncDecode(Image& image, ImageId image_id);
|
||||
void TickAsyncDecode();
|
||||
void EnforceSamplerBudget();
|
||||
void TrimInactiveSamplers(size_t budget);
|
||||
std::optional<size_t> QuerySamplerBudget() const;
|
||||
|
||||
void QueueAsyncUnswizzle(Image& image, ImageId image_id);
|
||||
void TickAsyncUnswizzle();
|
||||
@@ -506,6 +509,7 @@ private:
|
||||
|
||||
u64 modification_tick = 0;
|
||||
u64 frame_tick = 0;
|
||||
u64 last_sampler_gc_frame = (std::numeric_limits<u64>::max)();
|
||||
|
||||
Common::ThreadWorker texture_decode_worker{1, "TextureDecoder", {},
|
||||
Common::ThreadPlacement::Efficiency};
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
#include "common/cityhash.h"
|
||||
#include "common/settings.h"
|
||||
@@ -18,25 +17,53 @@ namespace Tegra::Texture {
|
||||
|
||||
namespace {
|
||||
|
||||
float SrgbToLinear(u32 value) {
|
||||
const float encoded = static_cast<float>(value) / 255.0f;
|
||||
if (encoded <= 0.04045f) {
|
||||
return encoded / 12.92f;
|
||||
}
|
||||
return std::pow((encoded + 0.055f) / 1.055f, 2.4f);
|
||||
}
|
||||
[[maybe_unused]] constexpr std::array<float, 256> SRGB_CONVERSION_LUT = {
|
||||
0.000000f, 0.000000f, 0.000000f, 0.000012f, 0.000021f, 0.000033f, 0.000046f, 0.000062f,
|
||||
0.000081f, 0.000102f, 0.000125f, 0.000151f, 0.000181f, 0.000214f, 0.000251f, 0.000293f,
|
||||
0.000338f, 0.000388f, 0.000443f, 0.000503f, 0.000568f, 0.000639f, 0.000715f, 0.000798f,
|
||||
0.000887f, 0.000983f, 0.001085f, 0.001195f, 0.001312f, 0.001437f, 0.001569f, 0.001710f,
|
||||
0.001860f, 0.002019f, 0.002186f, 0.002364f, 0.002551f, 0.002748f, 0.002955f, 0.003174f,
|
||||
0.003403f, 0.003643f, 0.003896f, 0.004160f, 0.004436f, 0.004725f, 0.005028f, 0.005343f,
|
||||
0.005672f, 0.006015f, 0.006372f, 0.006744f, 0.007130f, 0.007533f, 0.007950f, 0.008384f,
|
||||
0.008834f, 0.009301f, 0.009785f, 0.010286f, 0.010805f, 0.011342f, 0.011898f, 0.012472f,
|
||||
0.013066f, 0.013680f, 0.014313f, 0.014967f, 0.015641f, 0.016337f, 0.017054f, 0.017793f,
|
||||
0.018554f, 0.019337f, 0.020144f, 0.020974f, 0.021828f, 0.022706f, 0.023609f, 0.024536f,
|
||||
0.025489f, 0.026468f, 0.027473f, 0.028504f, 0.029563f, 0.030649f, 0.031762f, 0.032904f,
|
||||
0.034074f, 0.035274f, 0.036503f, 0.037762f, 0.039050f, 0.040370f, 0.041721f, 0.043103f,
|
||||
0.044518f, 0.045964f, 0.047444f, 0.048956f, 0.050503f, 0.052083f, 0.053699f, 0.055349f,
|
||||
0.057034f, 0.058755f, 0.060513f, 0.062307f, 0.064139f, 0.066008f, 0.067915f, 0.069861f,
|
||||
0.071845f, 0.073869f, 0.075933f, 0.078037f, 0.080182f, 0.082369f, 0.084597f, 0.086867f,
|
||||
0.089180f, 0.091535f, 0.093935f, 0.096378f, 0.098866f, 0.101398f, 0.103977f, 0.106601f,
|
||||
0.109271f, 0.111988f, 0.114753f, 0.117565f, 0.120426f, 0.123335f, 0.126293f, 0.129301f,
|
||||
0.132360f, 0.135469f, 0.138629f, 0.141841f, 0.145105f, 0.148421f, 0.151791f, 0.155214f,
|
||||
0.158691f, 0.162224f, 0.165810f, 0.169453f, 0.173152f, 0.176907f, 0.180720f, 0.184589f,
|
||||
0.188517f, 0.192504f, 0.196549f, 0.200655f, 0.204820f, 0.209046f, 0.213334f, 0.217682f,
|
||||
0.222093f, 0.226567f, 0.231104f, 0.235704f, 0.240369f, 0.245099f, 0.249894f, 0.254754f,
|
||||
0.259681f, 0.264674f, 0.269736f, 0.274864f, 0.280062f, 0.285328f, 0.290664f, 0.296070f,
|
||||
0.301546f, 0.307094f, 0.312713f, 0.318404f, 0.324168f, 0.330006f, 0.335916f, 0.341902f,
|
||||
0.347962f, 0.354097f, 0.360309f, 0.366597f, 0.372961f, 0.379403f, 0.385924f, 0.392524f,
|
||||
0.399202f, 0.405960f, 0.412798f, 0.419718f, 0.426719f, 0.433802f, 0.440967f, 0.448216f,
|
||||
0.455548f, 0.462965f, 0.470465f, 0.478052f, 0.485725f, 0.493484f, 0.501329f, 0.509263f,
|
||||
0.517285f, 0.525396f, 0.533595f, 0.541885f, 0.550265f, 0.558736f, 0.567299f, 0.575954f,
|
||||
0.584702f, 0.593542f, 0.602477f, 0.611507f, 0.620632f, 0.629852f, 0.639168f, 0.648581f,
|
||||
0.658092f, 0.667700f, 0.677408f, 0.687214f, 0.697120f, 0.707127f, 0.717234f, 0.727443f,
|
||||
0.737753f, 0.748167f, 0.758685f, 0.769305f, 0.780031f, 0.790861f, 0.801798f, 0.812839f,
|
||||
0.823989f, 0.835246f, 0.846611f, 0.858085f, 0.869668f, 0.881360f, 0.893164f, 0.905078f,
|
||||
0.917104f, 0.929242f, 0.941493f, 0.953859f, 0.966338f, 1.000000f, 1.000000f, 1.000000f,
|
||||
};
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::array<float, 4> TSCEntry::BorderColor() const noexcept {
|
||||
// TODO: Handle SRGB correctly. Using this breaks shadows in some games (Xenoblade).
|
||||
// if (!srgb_conversion) {
|
||||
// return border_color;
|
||||
//}
|
||||
// return {SRGB_CONVERSION_LUT[srgb_border_color_r], SRGB_CONVERSION_LUT[srgb_border_color_g],
|
||||
// SRGB_CONVERSION_LUT[srgb_border_color_b], border_color[3]};
|
||||
return border_color;
|
||||
}
|
||||
|
||||
std::array<float, 4> TSCEntry::SrgbBorderColor() const noexcept {
|
||||
return {SrgbToLinear(srgb_border_color_r), SrgbToLinear(srgb_border_color_g),
|
||||
SrgbToLinear(srgb_border_color_b), border_color[3]};
|
||||
}
|
||||
|
||||
float TSCEntry::MaxAnisotropy() const noexcept {
|
||||
const bool is_suitable_mipmap_filter = mipmap_filter != TextureMipmapFilter::None;
|
||||
const bool has_regular_lods = min_lod_clamp == 0 && max_lod_clamp >= 256;
|
||||
@@ -53,16 +80,16 @@ float TSCEntry::MaxAnisotropy() const noexcept {
|
||||
case Settings::AnisotropyMode::X4:
|
||||
case Settings::AnisotropyMode::X8:
|
||||
case Settings::AnisotropyMode::X16:
|
||||
added_anisotropic = s32(anisotropic_settings) - 1;
|
||||
case Settings::AnisotropyMode::X32:
|
||||
case Settings::AnisotropyMode::X64:
|
||||
added_anisotropic = u32(anisotropic_settings) - 1U;
|
||||
break;
|
||||
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);
|
||||
}
|
||||
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);
|
||||
break;
|
||||
}
|
||||
case Settings::AnisotropyMode::None:
|
||||
return 1.0f; //No use of anisotropy
|
||||
}
|
||||
return float(1U << (max_anisotropy + added_anisotropic));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -381,8 +378,6 @@ struct TSCEntry {
|
||||
|
||||
std::array<float, 4> BorderColor() const noexcept;
|
||||
|
||||
std::array<float, 4> SrgbBorderColor() const noexcept;
|
||||
|
||||
float MaxAnisotropy() const noexcept;
|
||||
|
||||
float MinLod() const {
|
||||
|
||||
@@ -508,9 +508,17 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers require scaled vertex format emulation.");
|
||||
has_broken_descriptor_aliasing = true;
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken descriptor aliasing.");
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken custom border color.");
|
||||
RemoveExtensionFeature(extensions.custom_border_color, features.custom_border_color,
|
||||
VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken border color swizzle.");
|
||||
RemoveExtensionFeature(extensions.border_color_swizzle, features.border_color_swizzle,
|
||||
VK_EXT_BORDER_COLOR_SWIZZLE_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken color write enable.");
|
||||
RemoveExtensionFeature(extensions.color_write_enable, features.color_write_enable,
|
||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader float controls.");
|
||||
RemoveExtension(extensions.shader_float_controls, VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
|
||||
LOG_WARNING(Render_Vulkan, "Qualcomm drivers have broken shader atomic int64.");
|
||||
RemoveExtensionFeature(extensions.shader_atomic_int64, features.shader_atomic_int64,
|
||||
VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME);
|
||||
@@ -611,6 +619,21 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
|
||||
}
|
||||
}
|
||||
|
||||
if (is_qualcomm) {
|
||||
const size_t sampler_limit = properties.properties.limits.maxSamplerAllocationCount;
|
||||
if (sampler_limit > 0) {
|
||||
constexpr size_t MIN_SAMPLER_BUDGET = 1024U;
|
||||
const size_t reserved = sampler_limit / 4U;
|
||||
const size_t derived_budget =
|
||||
(std::max)(MIN_SAMPLER_BUDGET, sampler_limit - reserved);
|
||||
sampler_heap_budget = derived_budget;
|
||||
LOG_WARNING(Render_Vulkan,
|
||||
"Qualcomm driver reports max {} samplers; reserving {} (25%) and "
|
||||
"allowing Eden to use {} (75%) to avoid heap exhaustion",
|
||||
sampler_limit, reserved, sampler_heap_budget);
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions.sampler_filter_minmax && is_amd) {
|
||||
// Disable ext_sampler_filter_minmax on AMD GCN4 and lower as it is broken.
|
||||
if (!features.shader_float16_int8.shaderFloat16) {
|
||||
@@ -1123,11 +1146,6 @@ bool Device::GetSuitability(bool requires_swapchain) {
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_5_PROPERTIES_KHR;
|
||||
SetNext(next, properties.maintenance5);
|
||||
}
|
||||
if (extensions.custom_border_color) {
|
||||
properties.custom_border_color.sType =
|
||||
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
|
||||
SetNext(next, properties.custom_border_color);
|
||||
}
|
||||
|
||||
// Perform the property fetch.
|
||||
physical.GetProperties2(properties2);
|
||||
@@ -1224,7 +1242,9 @@ void Device::RemoveUnsuitableExtensions() {
|
||||
// VK_EXT_border_color_swizzle
|
||||
if (extensions.border_color_swizzle) {
|
||||
extensions.border_color_swizzle =
|
||||
extensions.custom_border_color && features.border_color_swizzle.borderColorSwizzle;
|
||||
extensions.custom_border_color &&
|
||||
features.border_color_swizzle.borderColorSwizzle &&
|
||||
features.border_color_swizzle.borderColorSwizzleFromImage;
|
||||
}
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.border_color_swizzle,
|
||||
features.border_color_swizzle,
|
||||
@@ -1470,26 +1490,11 @@ void Device::SetupFamilies(VkSurfaceKHR surface) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Device::TryReserveCustomBorderColorSamplers(size_t count) const {
|
||||
const size_t limit = properties.custom_border_color.maxCustomBorderColorSamplers;
|
||||
if (limit == 0) {
|
||||
return true;
|
||||
std::optional<size_t> Device::GetSamplerHeapBudget() const {
|
||||
if (sampler_heap_budget == 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
size_t used = custom_border_color_samplers_used.load(std::memory_order_relaxed);
|
||||
while (used + count <= limit) {
|
||||
if (custom_border_color_samplers_used.compare_exchange_weak(
|
||||
used, used + count, std::memory_order_relaxed, std::memory_order_relaxed)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Device::ReleaseCustomBorderColorSamplers(size_t count) const {
|
||||
if (count == 0) {
|
||||
return;
|
||||
}
|
||||
custom_border_color_samplers_used.fetch_sub(count, std::memory_order_relaxed);
|
||||
return sampler_heap_budget;
|
||||
}
|
||||
|
||||
u64 Device::GetDeviceMemoryUsage() const {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <span>
|
||||
@@ -354,7 +353,6 @@ public:
|
||||
|
||||
#define FN_MAX_LIMIT_LIST \
|
||||
FN_MAX_LIMIT_ELEM(ComputeSharedMemorySize) \
|
||||
FN_MAX_LIMIT_ELEM(ComputeWorkGroupInvocations) \
|
||||
FN_MAX_LIMIT_ELEM(PerStageDescriptorSampledImages) \
|
||||
FN_MAX_LIMIT_ELEM(PerStageResources) \
|
||||
FN_MAX_LIMIT_ELEM(DescriptorSetSamplers) \
|
||||
@@ -701,18 +699,20 @@ FN_MAX_LIMIT_LIST
|
||||
return features.transform_feedback.geometryStreams;
|
||||
}
|
||||
|
||||
/// Returns true if custom border colors can be created without a format.
|
||||
bool IsCustomBorderColorUsable() const {
|
||||
return extensions.custom_border_color &&
|
||||
features.custom_border_color.customBorderColors &&
|
||||
features.custom_border_color.customBorderColorWithoutFormat;
|
||||
/// Returns true if the device supports VK_EXT_custom_border_color.
|
||||
bool IsExtCustomBorderColorSupported() const {
|
||||
return extensions.custom_border_color;
|
||||
}
|
||||
|
||||
/// Takes budget for samplers carrying a custom border color, false when exhausted.
|
||||
bool TryReserveCustomBorderColorSamplers(size_t count) const;
|
||||
/// Returns true if customBorderColors feature is available.
|
||||
bool IsCustomBorderColorsSupported() const {
|
||||
return features.custom_border_color.customBorderColors;
|
||||
}
|
||||
|
||||
/// Gives back budget taken by TryReserveCustomBorderColorSamplers.
|
||||
void ReleaseCustomBorderColorSamplers(size_t count) const;
|
||||
/// Returns true if customBorderColorWithoutFormat feature is available.
|
||||
bool IsCustomBorderColorWithoutFormatSupported() const {
|
||||
return features.custom_border_color.customBorderColorWithoutFormat;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_color_write_enable.
|
||||
bool IsExtColorWriteEnableSupported() const {
|
||||
@@ -724,12 +724,6 @@ FN_MAX_LIMIT_LIST
|
||||
return extensions.border_color_swizzle;
|
||||
}
|
||||
|
||||
/// Returns true if samplers must be carried with border color swizzle mapping.
|
||||
bool NeedsBorderColorSwizzleMapping() const {
|
||||
return extensions.border_color_swizzle &&
|
||||
!features.border_color_swizzle.borderColorSwizzleFromImage;
|
||||
}
|
||||
|
||||
/// Returns true if borderColorSwizzleFromImage is available.
|
||||
bool IsBorderColorSwizzleFromImageSupported() const {
|
||||
return features.border_color_swizzle.borderColorSwizzleFromImage;
|
||||
@@ -925,6 +919,8 @@ FN_MAX_LIMIT_LIST
|
||||
return has_broken_parallel_compiling;
|
||||
}
|
||||
|
||||
std::optional<size_t> GetSamplerHeapBudget() const;
|
||||
|
||||
/// Returns the vendor name reported from Vulkan.
|
||||
std::string_view GetVendorName() const {
|
||||
return properties.driver.driverName;
|
||||
@@ -1177,7 +1173,6 @@ private:
|
||||
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
|
||||
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
|
||||
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
|
||||
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};
|
||||
|
||||
VkPhysicalDeviceProperties properties{};
|
||||
};
|
||||
@@ -1216,7 +1211,7 @@ private:
|
||||
bool dynamic_state3_alpha_to_coverage{};
|
||||
bool dynamic_state3_alpha_to_one{};
|
||||
bool supports_conditional_barriers{}; ///< Allows barriers in conditional control flow.
|
||||
mutable std::atomic<size_t> custom_border_color_samplers_used{};
|
||||
size_t sampler_heap_budget{}; ///< Sampler budget for buggy drivers (0 = unlimited).
|
||||
u64 device_access_memory{}; ///< Total size of device local memory in bytes.
|
||||
u32 sets_per_pool{}; ///< Sets per Description Pool
|
||||
NvidiaArchitecture nvidia_arch{NvidiaArchitecture::Arch_AmpereOrNewer};
|
||||
|
||||
Reference in New Issue
Block a user