mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-28 17:45:39 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4648d406b4 | |||
| 926cc3185f | |||
| cc62c5644e | |||
| d81579ba51 |
@@ -188,7 +188,7 @@ android {
|
||||
create("mainline") {
|
||||
dimension = "version"
|
||||
isDefault = true
|
||||
minSdk = 33
|
||||
minSdk = 30
|
||||
|
||||
manifestPlaceholders += mapOf("appNameBase" to "Eden")
|
||||
resValue("string", "app_name_suffixed", "Eden")
|
||||
|
||||
+1
@@ -11,6 +11,7 @@ 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"),
|
||||
|
||||
+7
@@ -1032,6 +1032,13 @@ 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,
|
||||
|
||||
+1
@@ -1322,6 +1322,7 @@ class SettingsFragmentPresenter(
|
||||
add(HeaderSetting(R.string.log))
|
||||
|
||||
add(BooleanSetting.DEBUG_FLUSH_BY_LINE.key)
|
||||
add(StringSetting.LOG_FILTER.key)
|
||||
}
|
||||
|
||||
add(HeaderSetting(R.string.general))
|
||||
|
||||
@@ -636,6 +636,8 @@
|
||||
<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>
|
||||
|
||||
@@ -19,23 +19,6 @@
|
||||
namespace AudioCore::Sink {
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] bool InitializeAudio() {
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
// See https://github.com/PCSX2/pcsx2/pull/12312
|
||||
// "SDL and cubeb backends previously resulted in different names for the output which
|
||||
// caused them be identified as different applications by the OS."
|
||||
//
|
||||
// Keep in sync with cubeb_sink.cpp name.
|
||||
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SDL_AudioDeviceID FindAudioDeviceByName(const std::string& device_name, bool capture) {
|
||||
int device_count = 0;
|
||||
SDL_AudioDeviceID* devices = capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||
@@ -221,14 +204,20 @@ private:
|
||||
};
|
||||
|
||||
SDLSink::SDLSink(std::string_view target_device_name) {
|
||||
if (InitializeAudio()) {
|
||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||
output_device = target_device_name;
|
||||
} else {
|
||||
output_device.clear();
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
device_channels = 2;
|
||||
}
|
||||
|
||||
if (target_device_name != auto_device_name && !target_device_name.empty()) {
|
||||
output_device = target_device_name;
|
||||
} else {
|
||||
output_device.clear();
|
||||
}
|
||||
|
||||
device_channels = 2;
|
||||
}
|
||||
|
||||
SDLSink::~SDLSink() = default;
|
||||
@@ -276,10 +265,15 @@ void SDLSink::SetSystemVolume(f32 volume) {
|
||||
}
|
||||
|
||||
std::vector<std::string> ListSDLSinkDevices(bool capture) {
|
||||
if (!InitializeAudio())
|
||||
return {}; //no devices
|
||||
|
||||
std::vector<std::string> device_list;
|
||||
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
int device_count = 0;
|
||||
SDL_AudioDeviceID* devices =
|
||||
capture ? SDL_GetAudioRecordingDevices(&device_count)
|
||||
@@ -310,8 +304,13 @@ bool IsSDLSuitable() {
|
||||
return false;
|
||||
#else
|
||||
// Check SDL can init
|
||||
if (!InitializeAudio()!
|
||||
return false;
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
LOG_ERROR(Audio_Sink, "SDL failed to init, it is not suitable. Error: {}",
|
||||
SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We can set any latency frequency we want with SDL, so no need to check that.
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ struct ColorConsoleBackend final : public Backend {
|
||||
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];
|
||||
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);
|
||||
@@ -425,14 +425,14 @@ void SetColorConsoleBackendEnabled(bool enabled) {
|
||||
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename, unsigned int line_num, const char* function, fmt::string_view format, const fmt::format_args& args) {
|
||||
if (logging_instance && logging_instance->filter.CheckMessage(log_class, log_level)) {
|
||||
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';
|
||||
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)(result.size, sizeof(buffer) - 1),
|
||||
.message_len = (std::min)(sizeof(buffer) - 1, result.size),
|
||||
.timestamp = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - logging_instance->time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -904,7 +904,7 @@ struct Values {
|
||||
0,
|
||||
65535,
|
||||
"debug_knobs",
|
||||
Category::Debugging,
|
||||
Category::System,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
true};
|
||||
|
||||
@@ -7,4 +7,6 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION 1
|
||||
#define STB_IMAGE_RESIZE_IMPLEMENTATION 1
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION 1
|
||||
#define STBI_ONLY_JPEG 1
|
||||
|
||||
#include "common/stb.h"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#pragma once
|
||||
|
||||
#define STBI_ONLY_JPEG 1
|
||||
#define STBI_WRITE_NO_STDIO 1
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
+182
-14
@@ -3,14 +3,158 @@
|
||||
// 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);
|
||||
@@ -26,21 +170,45 @@ void ArmInterface::LogBacktrace(Kernel::KProcess* process) const {
|
||||
ctx.r[28], ctx.fp, ctx.lr, ctx.sp,
|
||||
};
|
||||
|
||||
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");
|
||||
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");
|
||||
}
|
||||
|
||||
auto const backtrace = GetBacktraceFromContext(process, ctx);
|
||||
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);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
const Kernel::DebugWatchpoint* ArmInterface::MatchingWatchpoint(
|
||||
|
||||
+6
-34
@@ -4,7 +4,6 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "game_settings.h"
|
||||
@@ -249,30 +248,12 @@ struct System::Impl {
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyNVDECChannelOpen(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
++nvdec_active_channels[process_id];
|
||||
}
|
||||
|
||||
void NotifyNVDECChannelClose(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
const auto it = nvdec_active_channels.find(process_id);
|
||||
if (it == nvdec_active_channels.end()) {
|
||||
return;
|
||||
}
|
||||
if (--it->second == 0) {
|
||||
nvdec_active_channels.erase(it);
|
||||
}
|
||||
void SetNVDECActive(bool is_nvdec_active) {
|
||||
nvdec_active = is_nvdec_active;
|
||||
}
|
||||
|
||||
bool GetNVDECActive() {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
return !nvdec_active_channels.empty();
|
||||
}
|
||||
|
||||
bool IsNVDECActiveForProcess(u64 process_id) {
|
||||
std::scoped_lock lock{nvdec_active_mutex};
|
||||
return nvdec_active_channels.contains(process_id);
|
||||
return nvdec_active;
|
||||
}
|
||||
|
||||
void InitializeDebugger(System& system, u16 port) {
|
||||
@@ -517,8 +498,6 @@ struct System::Impl {
|
||||
|
||||
mutable std::mutex suspend_guard;
|
||||
std::mutex general_channel_mutex;
|
||||
std::mutex nvdec_active_mutex;
|
||||
std::unordered_map<u64, u32> nvdec_active_channels;
|
||||
std::atomic_bool is_paused{};
|
||||
std::atomic_bool is_shutting_down{};
|
||||
std::atomic_bool is_powered_on{};
|
||||
@@ -526,6 +505,7 @@ struct System::Impl {
|
||||
bool extended_memory_layout : 1 = false;
|
||||
bool exit_locked : 1 = false;
|
||||
bool exit_requested : 1 = false;
|
||||
bool nvdec_active : 1 = false;
|
||||
|
||||
void EnsureGeneralChannelInitialized(System& system) {
|
||||
if (!general_channel_event) {
|
||||
@@ -589,22 +569,14 @@ void System::UnstallApplication() {
|
||||
impl->UnstallApplication();
|
||||
}
|
||||
|
||||
void System::NotifyNVDECChannelOpen(u64 process_id) {
|
||||
impl->NotifyNVDECChannelOpen(process_id);
|
||||
}
|
||||
|
||||
void System::NotifyNVDECChannelClose(u64 process_id) {
|
||||
impl->NotifyNVDECChannelClose(process_id);
|
||||
void System::SetNVDECActive(bool is_nvdec_active) {
|
||||
impl->SetNVDECActive(is_nvdec_active);
|
||||
}
|
||||
|
||||
bool System::GetNVDECActive() {
|
||||
return impl->GetNVDECActive();
|
||||
}
|
||||
|
||||
bool System::IsNVDECActiveForProcess(u64 process_id) {
|
||||
return impl->IsNVDECActiveForProcess(process_id);
|
||||
}
|
||||
|
||||
void System::InitializeDebugger() {
|
||||
impl->InitializeDebugger(*this, Settings::values.gdbstub_port.GetValue());
|
||||
}
|
||||
|
||||
+1
-3
@@ -191,10 +191,8 @@ public:
|
||||
std::unique_lock<std::mutex> StallApplication();
|
||||
void UnstallApplication();
|
||||
|
||||
void NotifyNVDECChannelOpen(u64 process_id);
|
||||
void NotifyNVDECChannelClose(u64 process_id);
|
||||
void SetNVDECActive(bool is_nvdec_active);
|
||||
[[nodiscard]] bool GetNVDECActive();
|
||||
[[nodiscard]] bool IsNVDECActiveForProcess(u64 process_id);
|
||||
|
||||
/**
|
||||
* Initialize the debugger.
|
||||
|
||||
@@ -324,7 +324,6 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
||||
|
||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||
LOG_WARNING(Service_AM, "(stubbed)");
|
||||
R_UNLESS(out_pseudo_device_id, ResultUnknown);
|
||||
|
||||
// This should be hashed with the device specific hash
|
||||
// for now this will do
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
// 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"
|
||||
@@ -30,6 +33,15 @@ 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);
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "common/stb.h"
|
||||
#define STBI_ONLY_JPEG 1
|
||||
#include <stb_image.h>
|
||||
#include <stb_image_resize.h>
|
||||
#include <stb_image_write.h>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/devices/ioctl_serialization.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
|
||||
@@ -72,23 +71,17 @@ NvResult nvhost_nvdec::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in
|
||||
|
||||
void nvhost_nvdec::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream started");
|
||||
system.SetNVDECActive(true);
|
||||
sessions[fd] = session_id;
|
||||
if (const auto* session = core.GetSession(session_id);
|
||||
session != nullptr && session->process != nullptr) {
|
||||
system.NotifyNVDECChannelOpen(session->process->GetId());
|
||||
}
|
||||
host1x.StartDevice(fd, Tegra::Host1x::ChannelType::NvDec, channel_syncpoint);
|
||||
}
|
||||
|
||||
void nvhost_nvdec::OnClose(DeviceFD fd) {
|
||||
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
|
||||
host1x.StopDevice(fd, Tegra::Host1x::ChannelType::NvDec);
|
||||
system.SetNVDECActive(false);
|
||||
auto it = sessions.find(fd);
|
||||
if (it != sessions.end()) {
|
||||
if (const auto* session = core.GetSession(it->second);
|
||||
session != nullptr && session->process != nullptr) {
|
||||
system.NotifyNVDECChannelClose(session->process->GetId());
|
||||
}
|
||||
sessions.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,16 +4,12 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <fmt/ranges.h>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/service.h"
|
||||
@@ -37,7 +33,6 @@ ServiceFrameworkBase::ServiceFrameworkBase(Core::System& system_, const char* se
|
||||
: SessionRequestHandler(system_.Kernel(), service_name_)
|
||||
, system{system_}
|
||||
, service_name{service_name_}
|
||||
, is_i_storage{std::string_view{service_name_} == "IStorage"}
|
||||
, handler_invoker{handler_invoker_}
|
||||
, max_sessions{max_sessions_}
|
||||
{}
|
||||
@@ -82,22 +77,13 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(HLERequestContext& ctx,
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::InvokeRequest(HLERequestContext& ctx) {
|
||||
const auto command = ctx.GetCommand();
|
||||
auto it = handlers.find(command);
|
||||
const bool is_cmd_read = command == 0;
|
||||
auto it = handlers.find(ctx.GetCommand());
|
||||
FunctionInfoBase const* info = it == handlers.end() ? nullptr : &it->second;
|
||||
if (info == nullptr || info->handler_callback == nullptr)
|
||||
return ReportUnimplementedFunction(ctx, info);
|
||||
|
||||
LOG_TRACE(Service, "{}", MakeFunctionString(info->name, GetServiceName(), ctx.CommandBuffer()));
|
||||
handler_invoker(this, info->handler_callback, ctx);
|
||||
|
||||
if (is_i_storage && is_cmd_read) {
|
||||
const auto* const process = ctx.GetThread().GetOwnerProcess();
|
||||
if (process != nullptr && system.IsNVDECActiveForProcess(process->GetId())) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds{600});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::InvokeRequestTipc(HLERequestContext& ctx) {
|
||||
|
||||
@@ -107,8 +107,6 @@ protected:
|
||||
Core::System& system;
|
||||
/// Identifier string used to connect to the service.
|
||||
const char* service_name;
|
||||
/// Whether this is the IStorage service.
|
||||
const bool is_i_storage;
|
||||
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
|
||||
InvokerFn* handler_invoker;
|
||||
/// Maximum number of concurrent sessions that this service can handle.
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/common_types.h"
|
||||
#include "dynarmic/backend/exception_handler.h"
|
||||
#include "dynarmic/common/context.h"
|
||||
@@ -54,21 +53,23 @@ class SigHandler {
|
||||
return e.first <= offset && e.first + e.second.size > offset;
|
||||
});
|
||||
}
|
||||
static void SigAction(int sig, siginfo_t* info, void* raw_context);
|
||||
|
||||
bool supports_fast_mem = true;
|
||||
void* signal_stack_memory = nullptr;
|
||||
ankerl::unordered_dense::map<u64, CodeBlockInfo> code_block_infos;
|
||||
std::shared_mutex code_block_infos_mutex;
|
||||
struct sigaction old_sa_segv;
|
||||
struct sigaction old_sa_bus;
|
||||
std::unique_ptr<uint8_t[]> signal_stack_memory;
|
||||
bool supports_fast_mem = true;
|
||||
std::size_t signal_stack_size;
|
||||
public:
|
||||
SigHandler() noexcept {
|
||||
auto const stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||
signal_stack_memory = std::make_unique<uint8_t[]>(stack_size);
|
||||
signal_stack_size = std::max<size_t>(SIGSTKSZ, 2 * 1024 * 1024);
|
||||
signal_stack_memory = mmap(nullptr, signal_stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
stack_t signal_stack{};
|
||||
signal_stack.ss_sp = signal_stack_memory.get();
|
||||
signal_stack.ss_size = stack_size;
|
||||
signal_stack.ss_sp = signal_stack_memory;
|
||||
signal_stack.ss_size = signal_stack_size;
|
||||
signal_stack.ss_flags = 0;
|
||||
if (sigaltstack(&signal_stack, nullptr) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: init failure at sigaltstack\n");
|
||||
@@ -86,7 +87,7 @@ public:
|
||||
supports_fast_mem = false;
|
||||
return;
|
||||
}
|
||||
#if defined(__APPLE__)
|
||||
#ifdef __APPLE__
|
||||
if (sigaction(SIGBUS, &sa, &old_sa_bus) != 0) {
|
||||
fmt::print(stderr, "dynarmic: POSIX SigHandler: could not set SIGBUS handler\n");
|
||||
supports_fast_mem = false;
|
||||
@@ -95,6 +96,10 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
~SigHandler() noexcept {
|
||||
munmap(signal_stack_memory, signal_stack_size);
|
||||
}
|
||||
|
||||
void AddCodeBlock(u64 offset, CodeBlockInfo cbi) noexcept {
|
||||
std::unique_lock guard(code_block_infos_mutex);
|
||||
code_block_infos.insert_or_assign(offset, cbi);
|
||||
@@ -104,17 +109,14 @@ public:
|
||||
code_block_infos.erase(offset);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool SupportsFastmem() const noexcept {
|
||||
return supports_fast_mem;
|
||||
}
|
||||
|
||||
static void RegisterHandler();
|
||||
static void SigAction(int sig, siginfo_t* info, void* raw_context);
|
||||
bool SupportsFastmem() const noexcept { return supports_fast_mem; }
|
||||
};
|
||||
|
||||
std::mutex handler_lock;
|
||||
std::optional<SigHandler> sig_handler;
|
||||
|
||||
void SigHandler::RegisterHandler() {
|
||||
void RegisterHandler() {
|
||||
std::lock_guard<std::mutex> guard(handler_lock);
|
||||
if (!sig_handler) {
|
||||
sig_handler.emplace();
|
||||
}
|
||||
@@ -123,27 +125,51 @@ void SigHandler::RegisterHandler() {
|
||||
void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
DEBUG_ASSERT(sig == SIGSEGV || sig == SIGBUS);
|
||||
CTX_DECLARE(raw_context);
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
{
|
||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||
if (auto const iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||
FakeCall fc = iter->second.cb(CTX_PC);
|
||||
#if defined(ARCHITECTURE_x86_64)
|
||||
CTX_SP -= sizeof(u64);
|
||||
*std::bit_cast<u64*>(CTX_SP) = fc.ret_rip;
|
||||
CTX_PC = fc.call_rip;
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
CTX_PC = fc.call_pc;
|
||||
#elif defined(ARCHITECTURE_riscv64)
|
||||
CTX_PC = fc.call_sepc;
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
CTX_PC = fc.call_pc;
|
||||
#else
|
||||
ASSERT(false);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG_ERROR(Core, "Unhandled {} at {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
fmt::print(stderr, "Unhandled {} at rip {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
#elif defined(ARCHITECTURE_arm64)
|
||||
{
|
||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||
FakeCall fc = iter->second.cb(CTX_PC);
|
||||
CTX_PC = fc.call_pc;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
#elif defined(ARCHITECTURE_riscv64)
|
||||
{
|
||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_SEPC); iter != sig_handler->code_block_infos.end()) {
|
||||
FakeCall fc = iter->second.cb(CTX_SEPC);
|
||||
CTX_SEPC = fc.call_sepc;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_SEPC);
|
||||
#elif defined(ARCHITECTURE_loongarch64)
|
||||
{
|
||||
std::shared_lock guard(sig_handler->code_block_infos_mutex);
|
||||
if (const auto iter = sig_handler->FindCodeBlockInfo(CTX_PC); iter != sig_handler->code_block_infos.end()) {
|
||||
FakeCall fc = iter->second.cb(CTX_PC);
|
||||
CTX_PC = fc.call_pc;
|
||||
return;
|
||||
}
|
||||
}
|
||||
fmt::print(stderr, "Unhandled {} at pc {:#018x}\n", sig == SIGSEGV ? "SIGSEGV" : "SIGBUS", CTX_PC);
|
||||
#else
|
||||
# error "Invalid architecture"
|
||||
#endif
|
||||
|
||||
struct sigaction* retry_sa = sig == SIGSEGV ? &sig_handler->old_sa_segv : &sig_handler->old_sa_bus;
|
||||
if (retry_sa->sa_flags & SA_SIGINFO) {
|
||||
@@ -164,10 +190,9 @@ void SigHandler::SigAction(int sig, siginfo_t* info, void* raw_context) {
|
||||
|
||||
struct ExceptionHandler::Impl final {
|
||||
Impl(u64 offset_, u64 size_)
|
||||
: offset(offset_)
|
||||
, size(size_)
|
||||
{
|
||||
SigHandler::RegisterHandler();
|
||||
: offset(offset_)
|
||||
, size(size_) {
|
||||
RegisterHandler();
|
||||
}
|
||||
|
||||
void SetCallback(std::function<FakeCall(u64)> cb) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <variant>
|
||||
#include "dynarmic/backend/loongarch64/emit_loongarch64.h"
|
||||
|
||||
#include "dynarmic/backend/loongarch64/a32_jitstate.h"
|
||||
@@ -96,8 +95,7 @@ EmittedBlockInfo EmitLoongArch64(lagoon_assembler_t& as, IR::Block block, const
|
||||
|
||||
// TODO: Emit Terminal
|
||||
const auto term = block.GetTerminal();
|
||||
const IR::Term::LeafTerminal* leaft_term = std::get_if<IR::Term::LeafTerminal>(&term);
|
||||
const IR::Term::LinkBlock* link_block_term = std::get_if<IR::Term::LinkBlock>(leaft_term);
|
||||
const IR::Term::LinkBlock* link_block_term = boost::get<IR::Term::LinkBlock>(&term);
|
||||
ASSERT(link_block_term);
|
||||
la_load_immediate64(&as, Xscratch0, link_block_term->next.Value());
|
||||
la_st_w(&as, Xscratch0, Xstate, static_cast<int32_t>(offsetof(A32JitState, regs) + sizeof(u32) * 15));
|
||||
|
||||
@@ -136,14 +136,14 @@
|
||||
# endif
|
||||
#elif defined(ARCHITECTURE_riscv64)
|
||||
# if defined(__FreeBSD__)
|
||||
# define CTX_PC (mctx.mc_gpregs.gp_sepc)
|
||||
# define CTX_SEPC (mctx.mc_gpregs.gp_sepc)
|
||||
# define CTX_SP (mctx.mc_gpregs.gp_sp)
|
||||
# elif defined(__linux__)
|
||||
# define CTX_PC (mctx.__gregs[REG_PC])
|
||||
# define CTX_SEPC (mctx.__gregs[REG_PC])
|
||||
# define CTX_SP (mctx.__gregs[REG_SP])
|
||||
# elif defined(__OpenBSD__)
|
||||
// https://github.com/openbsd/src/blob/master/sys/arch/riscv64/include/signal.h
|
||||
# define CTX_PC (ucontext->sc_sepc)
|
||||
# define CTX_SEPC (ucontext->sc_sepc)
|
||||
# define CTX_SP (ucontext->sc_sp)
|
||||
# else
|
||||
# error "unknown platform"
|
||||
|
||||
@@ -74,8 +74,8 @@ static constexpr char DEFAULT_DISCORD_IMAGE[] =
|
||||
"https://git.eden-emu.dev/eden-emu/eden/raw/branch/master/dist/qt_themes/default/icons/256x256/"
|
||||
"eden.png";
|
||||
|
||||
void DiscordImpl::UpdateGameStatus(std::string_view game_url, bool has_boxart) {
|
||||
const std::string url = std::string{has_boxart ? game_url : DEFAULT_DISCORD_IMAGE};
|
||||
void DiscordImpl::UpdateGameStatus(bool use_default) {
|
||||
const std::string url = use_default ? std::string{DEFAULT_DISCORD_IMAGE} : game_url;
|
||||
s64 start_time = std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch())
|
||||
.count();
|
||||
@@ -98,7 +98,7 @@ void DiscordImpl::Update() {
|
||||
|
||||
// Used to format Icon URL for yuzu website game compatibility page
|
||||
std::string icon_name = GetGameString(game_title);
|
||||
auto const game_url = fmt::format(
|
||||
game_url = fmt::format(
|
||||
"https://raw.githubusercontent.com/eden-emulator/boxart/refs/heads/master/img/{}.png",
|
||||
icon_name);
|
||||
|
||||
@@ -117,7 +117,7 @@ void DiscordImpl::Update() {
|
||||
};
|
||||
|
||||
auto res = client.send(request);
|
||||
UpdateGameStatus(game_url, res && res->status == 200);
|
||||
UpdateGameStatus(res && res->status == 200);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2018 Citra Emulator Project
|
||||
@@ -26,9 +26,11 @@ public:
|
||||
|
||||
private:
|
||||
std::string GetGameString(const std::string& title);
|
||||
void UpdateGameStatus(std::string_view game_url, bool use_default);
|
||||
void UpdateGameStatus(bool use_default);
|
||||
|
||||
std::string game_url{};
|
||||
std::string game_title{};
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 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 2021 yuzu Emulator Project
|
||||
@@ -665,8 +665,6 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU3
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 index,
|
||||
const IR::Value& clamp, const IR::Value& segmentation_mask);
|
||||
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 lane);
|
||||
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 direction);
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
||||
ScalarU32 swizzle);
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a);
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -100,24 +97,6 @@ void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, Sca
|
||||
Shuffle(ctx, inst, value, index, clamp, segmentation_mask, "XOR");
|
||||
}
|
||||
|
||||
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 lane) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("AND.U RC.x,{}.threadid,~3;"
|
||||
"AND.U RC.y,{},3;"
|
||||
"OR.U RC.x,RC.x,RC.y;"
|
||||
"SHFIDX.U {},{},RC.x,0x1C03;"
|
||||
"MOV.U {}.x,{}.y;",
|
||||
ctx.stage_name, lane, ret, value, ret, ret);
|
||||
}
|
||||
|
||||
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, ScalarU32 value, ScalarU32 direction) {
|
||||
const Register ret{ctx.reg_alloc.Define(inst)};
|
||||
ctx.Add("ADD.U RC.x,{},1;"
|
||||
"SHFXOR.U {},{},RC.x,0x1C03;"
|
||||
"MOV.U {}.x,{}.y;",
|
||||
direction, ret, value, ret, ret);
|
||||
}
|
||||
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, ScalarF32 op_a, ScalarF32 op_b,
|
||||
ScalarU32 swizzle) {
|
||||
const auto ret{ctx.reg_alloc.Define(inst)};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 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 2021 yuzu Emulator Project
|
||||
@@ -743,10 +743,6 @@ void EmitShuffleDown(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view index, std::string_view clamp,
|
||||
std::string_view segmentation_mask);
|
||||
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view lane);
|
||||
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view direction);
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
||||
std::string_view swizzle);
|
||||
void EmitDPdxFine(EmitContext& ctx, IR::Inst& inst, std::string_view op_a);
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -203,18 +200,6 @@ void EmitShuffleButterfly(EmitContext& ctx, IR::Inst& inst, std::string_view val
|
||||
ctx.AddU32("{}=shfl_in_bounds?shfl_result:{};", inst, value);
|
||||
}
|
||||
|
||||
void EmitQuadBroadcast(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view lane) {
|
||||
const auto src_thread_id{fmt::format("(({}&~3)|({}& 3))", THREAD_ID, lane)};
|
||||
ctx.AddU32("{}=readInvocationARB({},{});", inst, value, src_thread_id);
|
||||
}
|
||||
|
||||
void EmitQuadSwap(EmitContext& ctx, IR::Inst& inst, std::string_view value,
|
||||
std::string_view direction) {
|
||||
const auto src_thread_id{fmt::format("({}^({}+1))", THREAD_ID, direction)};
|
||||
ctx.AddU32("{}=readInvocationARB({},{});", inst, value, src_thread_id);
|
||||
}
|
||||
|
||||
void EmitFSwizzleAdd(EmitContext& ctx, IR::Inst& inst, std::string_view op_a, std::string_view op_b,
|
||||
std::string_view swizzle) {
|
||||
const auto mask{fmt::format("({}>>((gl_SubGroupInvocationARB&3)<<1))&3", swizzle)};
|
||||
|
||||
@@ -322,11 +322,6 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
|
||||
if (ctx.runtime_info.force_early_z) {
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests);
|
||||
}
|
||||
if (ctx.profile.support_shader_quad_control && program.info.uses_quad_shuffles) {
|
||||
ctx.AddExtension("SPV_KHR_quad_control");
|
||||
ctx.AddCapability(spv::Capability::QuadControlKHR);
|
||||
ctx.AddExecutionMode(main, spv::ExecutionMode::RequireFullQuadsKHR);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw NotImplementedException("Stage {}", program.stage);
|
||||
@@ -448,12 +443,6 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformVote);
|
||||
}
|
||||
}
|
||||
if (info.uses_quad_shuffles) {
|
||||
if (profile.support_quad_shuffles) {
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformQuad);
|
||||
}
|
||||
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
|
||||
}
|
||||
if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
|
||||
ctx.AddCapability(spv::Capability::Int64Atomics);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 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 2021 yuzu Emulator Project
|
||||
@@ -622,8 +622,6 @@ Id EmitShuffleDown(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clam
|
||||
Id segmentation_mask);
|
||||
Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id clamp,
|
||||
Id segmentation_mask);
|
||||
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane);
|
||||
Id EmitQuadSwap(EmitContext& ctx, Id value, Id direction);
|
||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle);
|
||||
Id EmitDPdxFine(EmitContext& ctx, Id op_a);
|
||||
Id EmitDPdyFine(EmitContext& ctx, Id op_a);
|
||||
|
||||
@@ -260,21 +260,6 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id
|
||||
return SelectValue(ctx, in_range, value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitQuadBroadcast(EmitContext& ctx, Id value, Id lane) {
|
||||
if (ctx.profile.support_quad_shuffles) {
|
||||
return ctx.OpGroupNonUniformQuadBroadcast(ctx.U32[1], SubgroupScope(ctx), value, lane);
|
||||
}
|
||||
const Id base{ctx.OpBitwiseAnd(ctx.U32[1], GetThreadId(ctx), ctx.Const(~3u))};
|
||||
const Id local_lane{ctx.OpBitwiseAnd(ctx.U32[1], lane, ctx.Const(3u))};
|
||||
const Id src_thread_id{ctx.OpBitwiseOr(ctx.U32[1], base, local_lane)};
|
||||
return ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id);
|
||||
}
|
||||
|
||||
Id EmitQuadSwap(EmitContext& ctx, Id value, Id direction) {
|
||||
const Id xor_mask{ctx.OpIAdd(ctx.U32[1], direction, ctx.Const(1u))};
|
||||
return ctx.OpGroupNonUniformShuffleXor(ctx.U32[1], SubgroupScope(ctx), value, xor_mask);
|
||||
}
|
||||
|
||||
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
|
||||
const Id three{ctx.Const(3U)};
|
||||
Id mask{GetThreadId(ctx)};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 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 2021 yuzu Emulator Project
|
||||
@@ -2100,14 +2100,6 @@ U32 IREmitter::ShuffleButterfly(const IR::U32& value, const IR::U32& index, cons
|
||||
return Inst<U32>(Opcode::ShuffleButterfly, value, index, clamp, seg_mask);
|
||||
}
|
||||
|
||||
U32 IREmitter::QuadBroadcast(const IR::U32& value, const IR::U32& lane) {
|
||||
return Inst<U32>(Opcode::QuadBroadcast, value, lane);
|
||||
}
|
||||
|
||||
U32 IREmitter::QuadSwap(const IR::U32& value, const IR::U32& direction) {
|
||||
return Inst<U32>(Opcode::QuadSwap, value, direction);
|
||||
}
|
||||
|
||||
F32 IREmitter::FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle, FpControl control) {
|
||||
return Inst<F32>(Opcode::FSwizzleAdd, Flags{control}, a, b, swizzle);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 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 2021 yuzu Emulator Project
|
||||
@@ -394,8 +394,6 @@ public:
|
||||
const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 ShuffleButterfly(const IR::U32& value, const IR::U32& index,
|
||||
const IR::U32& clamp, const IR::U32& seg_mask);
|
||||
[[nodiscard]] U32 QuadBroadcast(const IR::U32& value, const IR::U32& lane);
|
||||
[[nodiscard]] U32 QuadSwap(const IR::U32& value, const IR::U32& direction);
|
||||
[[nodiscard]] F32 FSwizzleAdd(const F32& a, const F32& b, const U32& swizzle,
|
||||
FpControl control = {});
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Shader::IR {
|
||||
|
||||
namespace Detail {
|
||||
|
||||
OpcodeMeta META_TABLE[534] = {
|
||||
OpcodeMeta META_TABLE[532] = {
|
||||
#define OPCODE(name_token, type_token, ...) \
|
||||
{ \
|
||||
.name{#name_token}, \
|
||||
@@ -21,7 +21,7 @@ OpcodeMeta META_TABLE[534] = {
|
||||
#undef OPCODE
|
||||
};
|
||||
|
||||
u8 NUM_ARGS[534] = {
|
||||
u8 NUM_ARGS[532] = {
|
||||
#define OPCODE(name_token, type_token, ...) u8(CalculateNumArgsOf(Opcode::name_token)),
|
||||
#include "opcodes.inc"
|
||||
#undef OPCODE
|
||||
|
||||
@@ -57,12 +57,12 @@ static constexpr Type F64x2{Type::F64x2};
|
||||
static constexpr Type F64x3{Type::F64x3};
|
||||
static constexpr Type F64x4{Type::F64x4};
|
||||
|
||||
extern OpcodeMeta META_TABLE[534];
|
||||
extern OpcodeMeta META_TABLE[532];
|
||||
constexpr size_t CalculateNumArgsOf(Opcode op) noexcept {
|
||||
const auto& arg_types = META_TABLE[size_t(op)].arg_types;
|
||||
return size_t(std::distance(arg_types.begin(), std::ranges::find(arg_types, Type::Void)));
|
||||
}
|
||||
extern u8 NUM_ARGS[534];
|
||||
extern u8 NUM_ARGS[532];
|
||||
} // namespace Detail
|
||||
|
||||
/// Get return type of an opcode
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -582,8 +579,6 @@ OPCODE(ShuffleIndex, U32, U32,
|
||||
OPCODE(ShuffleUp, U32, U32, U32, U32, U32, )
|
||||
OPCODE(ShuffleDown, U32, U32, U32, U32, U32, )
|
||||
OPCODE(ShuffleButterfly, U32, U32, U32, U32, U32, )
|
||||
OPCODE(QuadBroadcast, U32, U32, U32, )
|
||||
OPCODE(QuadSwap, U32, U32, U32, )
|
||||
OPCODE(FSwizzleAdd, F32, F32, F32, U32, )
|
||||
OPCODE(DPdxFine, F32, F32, )
|
||||
OPCODE(DPdyFine, F32, F32, )
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// 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 2021 yuzu Emulator Project
|
||||
@@ -36,10 +36,7 @@ enum class ShuffleMode : u64 {
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 QUAD_MASK = (28u << 8) | 3u;
|
||||
|
||||
void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask,
|
||||
bool index_is_imm, u32 index_imm, bool mask_is_imm, u32 mask_imm) {
|
||||
void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32& mask) {
|
||||
union {
|
||||
u64 insn;
|
||||
BitField<0, 8, IR::Reg> dest_reg;
|
||||
@@ -48,21 +45,6 @@ void Shuffle(TranslatorVisitor& v, u64 insn, const IR::U32& index, const IR::U32
|
||||
BitField<48, 3, IR::Pred> pred;
|
||||
} const shfl{insn};
|
||||
|
||||
const bool is_quad_candidate{mask_is_imm && mask_imm == QUAD_MASK && index_is_imm &&
|
||||
v.env.ShaderStage() == Stage::Fragment};
|
||||
if (is_quad_candidate) {
|
||||
if (shfl.mode == ShuffleMode::IDX && index_imm <= 3) {
|
||||
v.X(shfl.dest_reg, v.ir.QuadBroadcast(v.X(shfl.src_reg), v.ir.Imm32(index_imm)));
|
||||
v.ir.SetPred(shfl.pred, v.ir.Imm1(true));
|
||||
return;
|
||||
}
|
||||
if (shfl.mode == ShuffleMode::BFLY && index_imm >= 1 && index_imm <= 3) {
|
||||
v.X(shfl.dest_reg, v.ir.QuadSwap(v.X(shfl.src_reg), v.ir.Imm32(index_imm - 1)));
|
||||
v.ir.SetPred(shfl.pred, v.ir.Imm1(true));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const IR::U32 result{ShuffleOperation(v.ir, v.X(shfl.src_reg), index, mask, shfl.mode)};
|
||||
v.ir.SetPred(shfl.pred, v.ir.GetInBoundsFromOp(result));
|
||||
v.X(shfl.dest_reg, result);
|
||||
@@ -77,14 +59,11 @@ void TranslatorVisitor::SHFL(u64 insn) {
|
||||
BitField<29, 1, u64> src_b_flag;
|
||||
BitField<34, 13, u64> src_b_imm;
|
||||
} const flags{insn};
|
||||
const bool index_is_imm{flags.src_a_flag != 0};
|
||||
const bool mask_is_imm{flags.src_b_flag != 0};
|
||||
const IR::U32 src_a{index_is_imm ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
|
||||
: GetReg20(insn)};
|
||||
const IR::U32 src_b{mask_is_imm ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
|
||||
: GetReg39(insn)};
|
||||
Shuffle(*this, insn, src_a, src_b, index_is_imm, static_cast<u32>(flags.src_a_imm),
|
||||
mask_is_imm, static_cast<u32>(flags.src_b_imm));
|
||||
const IR::U32 src_a{flags.src_a_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_a_imm))
|
||||
: GetReg20(insn)};
|
||||
const IR::U32 src_b{flags.src_b_flag != 0 ? ir.Imm32(static_cast<u32>(flags.src_b_imm))
|
||||
: GetReg39(insn)};
|
||||
Shuffle(*this, insn, src_a, src_b);
|
||||
}
|
||||
|
||||
} // namespace Shader::Maxwell
|
||||
|
||||
@@ -498,10 +498,6 @@ void VisitUsages(Info& info, IR::Inst& inst) {
|
||||
case IR::Opcode::ShuffleButterfly:
|
||||
info.uses_subgroup_shuffles = true;
|
||||
break;
|
||||
case IR::Opcode::QuadBroadcast:
|
||||
case IR::Opcode::QuadSwap:
|
||||
info.uses_quad_shuffles = true;
|
||||
break;
|
||||
case IR::Opcode::GetCbufU8:
|
||||
case IR::Opcode::GetCbufS8:
|
||||
case IR::Opcode::GetCbufU16:
|
||||
|
||||
@@ -37,8 +37,6 @@ struct Profile {
|
||||
bool support_explicit_workgroup_layout{};
|
||||
bool support_workgroup_layout_8bit_access{};
|
||||
bool support_workgroup_layout_16bit_access{};
|
||||
bool support_shader_quad_control{};
|
||||
bool support_quad_shuffles{};
|
||||
bool support_vote{};
|
||||
u32 supported_subgroup_stages{0x7F};
|
||||
bool support_viewport_index_layer_non_geometry{};
|
||||
|
||||
@@ -252,7 +252,6 @@ struct Info {
|
||||
bool uses_is_helper_invocation{};
|
||||
bool uses_subgroup_invocation_id{};
|
||||
bool uses_subgroup_shuffles{};
|
||||
bool uses_quad_shuffles{};
|
||||
std::array<bool, 30> uses_patches{};
|
||||
|
||||
std::array<Interpolation, 32> interpolation{};
|
||||
|
||||
@@ -404,8 +404,6 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
device.IsWorkgroupMemoryExplicitLayout8BitAccessSupported(),
|
||||
.support_workgroup_layout_16bit_access =
|
||||
device.IsWorkgroupMemoryExplicitLayout16BitAccessSupported(),
|
||||
.support_shader_quad_control = device.IsKhrShaderQuadControlSupported(),
|
||||
.support_quad_shuffles = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_QUAD_BIT),
|
||||
.support_vote = device.IsSubgroupFeatureSupported(VK_SUBGROUP_FEATURE_VOTE_BIT),
|
||||
.supported_subgroup_stages = supported_subgroup_stages,
|
||||
.support_viewport_index_layer_non_geometry =
|
||||
|
||||
@@ -1386,11 +1386,6 @@ void Device::RemoveUnsuitableExtensions() {
|
||||
VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
// VK_KHR_shader_quad_control
|
||||
extensions.shader_quad_control = features.shader_quad_control.shaderQuadControl;
|
||||
RemoveExtensionFeatureIfUnsuitable(extensions.shader_quad_control, features.shader_quad_control,
|
||||
VK_KHR_SHADER_QUAD_CONTROL_EXTENSION_NAME);
|
||||
|
||||
// VK_KHR_workgroup_memory_explicit_layout
|
||||
extensions.workgroup_memory_explicit_layout =
|
||||
features.workgroup_memory_explicit_layout.workgroupMemoryExplicitLayout &&
|
||||
|
||||
@@ -75,7 +75,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
FEATURE(KHR, Maintenance6, MAINTENANCE_6, maintenance6) \
|
||||
FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \
|
||||
pipeline_executable_properties) \
|
||||
FEATURE(KHR, ShaderQuadControl, SHADER_QUAD_CONTROL, shader_quad_control) \
|
||||
FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \
|
||||
workgroup_memory_explicit_layout)
|
||||
|
||||
@@ -587,11 +586,6 @@ FN_MAX_LIMIT_LIST
|
||||
features.features.shaderInt16;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_shader_quad_control.
|
||||
bool IsKhrShaderQuadControlSupported() const {
|
||||
return extensions.shader_quad_control && features.shader_quad_control.shaderQuadControl;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_KHR_image_format_list.
|
||||
bool IsKhrImageFormatListSupported() const {
|
||||
return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2;
|
||||
|
||||
Reference in New Issue
Block a user