Compare commits

..

1 Commits

Author SHA1 Message Date
CamilleLaVey 09e1a2da93 [TEST] Aniso fixes 2026-08-23 20:28:03 +00:00
30 changed files with 384 additions and 692 deletions
@@ -11,7 +11,6 @@ import org.yuzu.yuzu_emu.utils.NativeConfig
enum class StringSetting(override val key: String) : AbstractStringSetting {
DRIVER_PATH("driver_path"),
DEVICE_NAME("device_name"),
LOG_FILTER("log_filter"),
PROGRAM_ARGS("program_args"),
WEB_TOKEN("eden_token"),
@@ -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 = "%"
)
)
@@ -1032,13 +1031,6 @@ abstract class SettingsItem(
descriptionId = R.string.use_auto_stub_description
)
)
put(
StringInputSetting(
StringSetting.LOG_FILTER,
titleId = R.string.log_filter,
descriptionId = R.string.log_filter_description
)
)
put(
SpinBoxSetting(
ShortSetting.DEBUG_KNOBS,
@@ -1322,7 +1322,6 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.log))
add(BooleanSetting.DEBUG_FLUSH_BY_LINE.key)
add(StringSetting.LOG_FILTER.key)
}
add(HeaderSetting(R.string.general))
@@ -1182,7 +1182,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
container,
IntSetting.FSR_SHARPENING_SLIDER,
minValue = 0,
maxValue = 200,
maxValue = 100,
units = "%"
)
}
+17 -12
View File
@@ -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">
@@ -636,8 +636,6 @@
<string name="log">Logging</string>
<string name="flush_by_line">Flush debug logs by line</string>
<string name="flush_by_line_description">Flushes debugging logs on each line written, making debugging easier in cases of crashing or freezing.</string>
<string name="log_filter">Log filter</string>
<string name="log_filter_description">Controls Eden\'s log categories. Example: *:Info Service.LM:Debug</string>
<!-- GPU Logging strings -->
<string name="gpu_logging_header">GPU Logging</string>
+45 -56
View File
@@ -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[128];
auto result = fmt::format_to_n(buffer, sizeof(buffer) - 1, "\x1b{}[{:4d}.{:06d}] {} <{}> {}:{}:{}: ", color_str, df.time_seconds, df.time_fractional, df.class_name, df.level_name, entry.filename, entry.line_num, entry.function, entry.message);
std::fwrite(buffer, 1, (std::min)(sizeof(buffer) - 1, result.size), stdout);
std::fwrite(entry.message, 1, entry.message_len, stdout);
std::fwrite("\x1b[0m\n", 1, sizeof("\x1b[0m\n"), stdout);
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!
@@ -425,14 +418,10 @@ void SetColorConsoleBackendEnabled(bool enabled) {
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) {
if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) {
char buffer[BUFSIZ];
auto result = fmt::vformat_to_n(buffer, sizeof(buffer) - 1, format, args);
buffer[result.size] = '\0';
auto const flush = ::Settings::values.log_flush_line.GetValue();
logging_instance->ForEachBackend([=](Backend& backend) {
backend.Write(Entry{
.message = buffer,
.message_len = (std::min)(sizeof(buffer) - 1, result.size),
.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,
+21
View File
@@ -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
+1 -1
View File
@@ -904,7 +904,7 @@ struct Values {
0,
65535,
"debug_knobs",
Category::System,
Category::Debugging,
Specialization::Countable,
true,
true};
+14 -182
View File
@@ -3,158 +3,14 @@
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <array>
#include <cctype>
#include <string>
#include <string_view>
#include <vector>
#include "common/logging.h"
#include "core/arm/arm_interface.h"
#include "core/arm/debug.h"
#include "core/core.h"
#include "core/hle/kernel/k_memory_block.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/svc_types.h"
namespace Core {
namespace {
constexpr std::size_t GuestStringProbeBytes = 0x100;
constexpr std::size_t StackProbeWords = 96;
constexpr std::size_t MaxGuestStringLogs = 16;
constexpr std::size_t MaxBacktraceFrames = 64;
constexpr std::size_t MinGuestStringLength = 4;
std::string SanitizeGuestString(std::string_view text) {
std::string sanitized;
sanitized.reserve(text.size());
for (const char ch : text) {
switch (ch) {
case '\\':
sanitized += "\\\\";
break;
case '"':
sanitized += "\\\"";
break;
default:
sanitized += ch;
break;
}
}
return sanitized;
}
bool IsUsefulGuestString(std::string_view text) {
if (text.size() < MinGuestStringLength) {
return false;
}
std::size_t alpha_numeric_count{};
for (const char ch : text) {
const auto byte = static_cast<unsigned char>(ch);
if (std::isprint(byte) == 0) {
return false;
}
if (std::isalnum(byte) != 0) {
alpha_numeric_count++;
}
}
return alpha_numeric_count > 0;
}
bool QueryGuestMemoryInfo(Kernel::KProcess* process, u64 address,
Kernel::Svc::MemoryInfo* out_info) {
if (address == 0) {
return false;
}
Kernel::KMemoryInfo mem_info{};
Kernel::Svc::PageInfo page_info{};
if (process->GetPageTable().QueryInfo(&mem_info, &page_info, address).IsFailure()) {
return false;
}
*out_info = mem_info.GetSvcMemoryInfo();
return true;
}
void LogGuestStringCandidate(Kernel::KProcess* process, u64 address, std::string_view label,
std::vector<u64>& logged_strings) {
if (logged_strings.size() >= MaxGuestStringLogs) {
return;
}
if (std::find(logged_strings.begin(), logged_strings.end(), address) != logged_strings.end()) {
return;
}
Kernel::Svc::MemoryInfo mem_info{};
if (!QueryGuestMemoryInfo(process, address, &mem_info)) {
return;
}
if (mem_info.state == Kernel::Svc::MemoryState::Free ||
mem_info.permission == Kernel::Svc::MemoryPermission::None) {
return;
}
if (address < mem_info.base_address) {
return;
}
const u64 region_offset = address - mem_info.base_address;
if (region_offset >= mem_info.size) {
return;
}
const u64 available_bytes = mem_info.size - region_offset;
const auto probe_size =
static_cast<std::size_t>(std::min<u64>(GuestStringProbeBytes, available_bytes));
if (probe_size < MinGuestStringLength ||
!process->GetMemory().IsValidVirtualAddressRange(address, probe_size)) {
return;
}
const auto text = process->GetMemory().ReadCString(address, probe_size);
if (!IsUsefulGuestString(text)) {
return;
}
logged_strings.push_back(address);
LOG_ERROR(Core_ARM, "Guest backtrace string {:02}: {}={:016X} \"{}\"",
logged_strings.size() - 1, label, address, SanitizeGuestString(text));
}
void LogStackStringCandidates(Kernel::KProcess* process, u64 base, std::string_view label,
std::vector<u64>& logged_strings) {
if (base == 0) {
return;
}
auto& memory = process->GetMemory();
for (std::size_t i = 0; i < StackProbeWords; i++) {
const u64 address = base + i * sizeof(u64);
if (!memory.IsValidVirtualAddressRange(address, sizeof(u64))) {
break;
}
u64 value{};
if (!memory.ReadBlock(address, &value, sizeof(value))) {
continue;
}
LogGuestStringCandidate(process, value, fmt::format("{}+{:03X}", label, i * sizeof(u64)),
logged_strings);
}
}
} // namespace
void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
Kernel::Svc::ThreadContext ctx;
this->GetContext(ctx);
@@ -170,45 +26,21 @@ void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
ctx.r[28], ctx.fp, ctx.lr, ctx.sp,
};
LOG_ERROR(Core_ARM,
"Guest backtrace context: process=\"{}\" pid={} program_id={:016X} pc={:016X} "
"lr={:016X} fp={:016X} sp={:016X} pstate={:08X}",
process->GetName(), process->GetProcessId(), process->GetProgramId(), ctx.pc,
ctx.lr, ctx.fp, ctx.sp, ctx.pstate);
std::vector<u64> logged_strings;
for (size_t i = 0; i < xreg.size(); i++) {
LogGuestStringCandidate(process, xreg[i], fmt::format("R{:02}", i), logged_strings);
}
LogStackStringCandidates(process, ctx.sp, "SP", logged_strings);
if (ctx.fp != ctx.sp) {
LogStackStringCandidates(process, ctx.fp, "FP", logged_strings);
}
if (logged_strings.empty()) {
LOG_ERROR(Core_ARM, "Guest backtrace strings: none found in registers or stack");
}
std::string msg = fmt::format("Backtrace @ PC={:016X}\n", ctx.pc);
for (size_t i = 0; i < 32; i += 4)
msg += fmt::format("R{:02}={:016X} R{:02}={:016X} R{:02}={:016X} R{:02}={:016X}\n",
i + 0, xreg[i + 0], i + 1, xreg[i + 1],
i + 2, xreg[i + 2], i + 3, xreg[i + 3]);
for (size_t i = 0; i < 32; i += 2)
msg += fmt::format("V{:02}={:016X}_{:016X} V{:02}={:016X}_{:016X}\n",
i + 0, ctx.v[i + 0][0], ctx.v[i + 0][1],
i + 1, ctx.v[i + 1][0], ctx.v[i + 1][1]);
msg += fmt::format("PSTATE={:08X} FPCR={:08X} FPSR={:08X} TPIDR={:016X}\n", ctx.pstate, ctx.fpcr, ctx.fpsr, ctx.tpidr);
msg += fmt::format("{:20}{:20}{:20}{:20}{}\n", "Module", "Address", "Original Address", "Offset", "Symbol");
auto const backtrace = GetBacktraceFromContext(process, ctx);
for (size_t i = 0; i < std::min(backtrace.size(), MaxBacktraceFrames); i++) {
const auto& entry = backtrace[i];
if (entry.original_address == 0) {
break;
}
if (entry.name.empty()) {
LOG_ERROR(Core_ARM, "Guest backtrace frame {:03}: {}+0x{:X} pc={:016X} mapped={:016X}",
i, entry.module, entry.offset, entry.original_address, entry.address);
} else {
LOG_ERROR(Core_ARM,
"Guest backtrace frame {:03}: {}+0x{:X} pc={:016X} mapped={:016X} symbol={}",
i, entry.module, entry.offset, entry.original_address, entry.address,
entry.name);
}
}
if (backtrace.size() > MaxBacktraceFrames) {
LOG_ERROR(Core_ARM, "Guest backtrace truncated: logged={} total={}", MaxBacktraceFrames,
backtrace.size());
}
for (auto const& entry : backtrace)
msg += fmt::format("{:20}{:016X} {:016X} {:016X} {}\n", entry.module, entry.address, entry.original_address, entry.offset, entry.name);
LOG_ERROR(Core_ARM, "{}", msg);
}
const Kernel::DebugWatchpoint* ArmInterface::MatchingWatchpoint(
@@ -3,9 +3,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <chrono>
#include <thread>
#include "common/settings.h"
#include "core/file_sys/errors.h"
#include "core/hle/service/cmif_serialization.h"
@@ -33,15 +30,6 @@ Result IStorage::Read(
R_UNLESS(length >= 0, FileSys::ResultInvalidSize);
R_UNLESS(offset >= 0, FileSys::ResultInvalidOffset);
static thread_local std::chrono::steady_clock::time_point last_read_tick{};
const auto now = std::chrono::steady_clock::now();
const auto period = Settings::values.debug_knobs.GetValue();
const auto ReadInterval = std::chrono::microseconds{period};
if (last_read_tick != std::chrono::steady_clock::time_point{} &&
now - last_read_tick < ReadInterval) {
std::this_thread::sleep_for(ReadInterval - (now - last_read_tick));
}
last_read_tick = std::chrono::steady_clock::now();
// Read the data from the Storage backend
backend->Read(out_bytes.data(), length, offset);
+1 -6
View File
@@ -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));
+1 -6
View File
@@ -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));
@@ -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 {
-2
View File
@@ -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
@@ -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;
}
+14 -18
View File
@@ -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);
@@ -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 =
@@ -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;
@@ -1814,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();
}
@@ -2525,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;
@@ -2566,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) {
@@ -2735,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,
@@ -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);
@@ -444,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;
}
@@ -492,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 {
@@ -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};
+41 -14
View File
@@ -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;
-5
View File
@@ -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 {
+30 -25
View File
@@ -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 {
+14 -18
View File
@@ -6,7 +6,6 @@
#pragma once
#include <atomic>
#include <optional>
#include <set>
#include <span>
@@ -700,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 {
@@ -723,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;
@@ -924,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;
@@ -1176,7 +1173,6 @@ private:
VkPhysicalDeviceTransformFeedbackPropertiesEXT transform_feedback{};
VkPhysicalDeviceMaintenance5PropertiesKHR maintenance5{};
VkPhysicalDeviceDepthStencilResolveProperties depth_stencil_resolve{};
VkPhysicalDeviceCustomBorderColorPropertiesEXT custom_border_color{};
VkPhysicalDeviceProperties properties{};
};
@@ -1215,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};