mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-15 13:16:43 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ddd735922e | |||
| 25e6db0b36 | |||
| ba9130fbf9 | |||
| 7d5f390ffb | |||
| 8fe1e6efa2 | |||
| ee197e6222 | |||
| 612409c7ba | |||
| 54046ac60e |
@@ -65,7 +65,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "dev.eden.eden_emulator"
|
||||
minSdk = 33
|
||||
minSdk = 24
|
||||
targetSdk = 36
|
||||
versionName = getGitVersion()
|
||||
versionCode = autoVersion
|
||||
|
||||
+1
-1
@@ -594,7 +594,7 @@ abstract class SettingsItem(
|
||||
IntSetting.ANDROID_PIPELINE_WORKERS,
|
||||
titleId = R.string.pipeline_worker_cores,
|
||||
descriptionId = R.string.pipeline_worker_cores_description,
|
||||
min = 2,
|
||||
min = 4,
|
||||
max = 8,
|
||||
units = "cores"
|
||||
)
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
|
||||
@@ -169,7 +169,7 @@ class InputDialogFragment : DialogFragment() {
|
||||
NativeInput.onGamePadButtonEvent(
|
||||
controllerData.getGUID(),
|
||||
controllerData.getPort(),
|
||||
event.keyCode,
|
||||
InputHandler.getButtonIdFromEvent(event),
|
||||
action
|
||||
)
|
||||
onInputReceived(event.device)
|
||||
|
||||
@@ -49,6 +49,12 @@ object InputHandler {
|
||||
MotionEvent.AXIS_RTRIGGER
|
||||
)
|
||||
|
||||
// Currently, Android doesn't support Joy-Con D-pad buttons. We fall back to the scan code
|
||||
private const val LINUX_BUTTON_DPAD_UP = 0x220
|
||||
private const val LINUX_BUTTON_DPAD_DOWN = 0x221
|
||||
private const val LINUX_BUTTON_DPAD_LEFT = 0x222
|
||||
private const val LINUX_BUTTON_DPAD_RIGHT = 0x223
|
||||
|
||||
fun isPhysicalGameController(device: InputDevice?): Boolean {
|
||||
device ?: return false
|
||||
|
||||
@@ -87,12 +93,25 @@ object InputHandler {
|
||||
NativeInput.onGamePadButtonEvent(
|
||||
controllerData.getGUID(),
|
||||
controllerData.getPort(),
|
||||
event.keyCode,
|
||||
getButtonIdFromEvent(event),
|
||||
action
|
||||
)
|
||||
return true
|
||||
}
|
||||
|
||||
fun getButtonIdFromEvent(event: KeyEvent): Int {
|
||||
if (event.keyCode == 0) {
|
||||
return when (event.scanCode) {
|
||||
LINUX_BUTTON_DPAD_UP -> KeyEvent.KEYCODE_DPAD_UP
|
||||
LINUX_BUTTON_DPAD_DOWN -> KeyEvent.KEYCODE_DPAD_DOWN
|
||||
LINUX_BUTTON_DPAD_LEFT -> KeyEvent.KEYCODE_DPAD_LEFT
|
||||
LINUX_BUTTON_DPAD_RIGHT -> KeyEvent.KEYCODE_DPAD_RIGHT
|
||||
else -> return 0
|
||||
}
|
||||
}
|
||||
return event.keyCode
|
||||
}
|
||||
|
||||
fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
|
||||
val controllerData =
|
||||
androidControllers[event.device.controllerNumber] ?: return false
|
||||
|
||||
@@ -20,132 +20,96 @@ struct RomMetadata {
|
||||
std::vector<u8> icon;
|
||||
bool isHomebrew;
|
||||
};
|
||||
static ankerl::unordered_dense::map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||
|
||||
ankerl::unordered_dense::map<std::string, RomMetadata> m_rom_metadata_cache;
|
||||
static RomMetadata CacheRomMetadata(const std::string& path) {
|
||||
auto& instance = EmulationSession::GetInstance();
|
||||
const auto file = Core::GetGameFileFromPath(instance.System().GetFilesystem(), path);
|
||||
if (auto loader = Loader::GetLoader(instance.System(), file, 0, 0); loader) {
|
||||
RomMetadata entry;
|
||||
loader->ReadTitle(entry.title);
|
||||
loader->ReadProgramId(entry.programId);
|
||||
loader->ReadIcon(entry.icon);
|
||||
|
||||
RomMetadata CacheRomMetadata(const std::string& path) {
|
||||
const auto file =
|
||||
Core::GetGameFileFromPath(EmulationSession::GetInstance().System().GetFilesystem(), path);
|
||||
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file, 0, 0);
|
||||
const FileSys::PatchManager pm{
|
||||
entry.programId,
|
||||
instance.System().GetFileSystemController(),
|
||||
instance.System().GetContentProvider()
|
||||
};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
RomMetadata entry;
|
||||
loader->ReadTitle(entry.title);
|
||||
loader->ReadProgramId(entry.programId);
|
||||
loader->ReadIcon(entry.icon);
|
||||
|
||||
const FileSys::PatchManager pm{
|
||||
entry.programId, EmulationSession::GetInstance().System().GetFileSystemController(),
|
||||
EmulationSession::GetInstance().System().GetContentProvider()};
|
||||
const auto control = pm.GetControlMetadata();
|
||||
|
||||
if (control.first != nullptr) {
|
||||
entry.developer = control.first->GetDeveloperName();
|
||||
entry.version = control.first->GetVersionString();
|
||||
} else {
|
||||
FileSys::NACP nacp;
|
||||
if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
|
||||
entry.developer = nacp.GetDeveloperName();
|
||||
if (control.first != nullptr) {
|
||||
entry.developer = control.first->GetDeveloperName();
|
||||
entry.version = control.first->GetVersionString();
|
||||
} else {
|
||||
entry.developer = "";
|
||||
FileSys::NACP nacp{};
|
||||
entry.developer = loader->ReadControlData(nacp) == Loader::ResultStatus::Success
|
||||
? nacp.GetDeveloperName()
|
||||
: "";
|
||||
entry.version = "1.0.0";
|
||||
}
|
||||
|
||||
entry.version = "1.0.0";
|
||||
if (loader->GetFileType() == Loader::FileType::NRO) {
|
||||
auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
|
||||
entry.isHomebrew = loader_nro->IsHomebrew();
|
||||
} else {
|
||||
entry.isHomebrew = false;
|
||||
}
|
||||
m_rom_metadata_cache[path] = entry;
|
||||
return entry;
|
||||
}
|
||||
|
||||
if (loader->GetFileType() == Loader::FileType::NRO) {
|
||||
auto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get());
|
||||
entry.isHomebrew = loader_nro->IsHomebrew();
|
||||
} else {
|
||||
entry.isHomebrew = false;
|
||||
}
|
||||
|
||||
m_rom_metadata_cache[path] = entry;
|
||||
|
||||
return entry;
|
||||
return {};
|
||||
}
|
||||
|
||||
RomMetadata GetRomMetadata(const std::string& path, bool reload = false) {
|
||||
if (reload) {
|
||||
static RomMetadata GetRomMetadata(const std::string& path, bool reload = false) {
|
||||
if (reload)
|
||||
return CacheRomMetadata(path);
|
||||
}
|
||||
|
||||
if (auto search = m_rom_metadata_cache.find(path); search != m_rom_metadata_cache.end()) {
|
||||
return search->second;
|
||||
}
|
||||
|
||||
if (auto it = m_rom_metadata_cache.find(path); it != m_rom_metadata_cache.end())
|
||||
return it->second;
|
||||
return CacheRomMetadata(path);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||
Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read);
|
||||
if (!file) {
|
||||
return false;
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj, jstring jpath) {
|
||||
if (auto const file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(Common::Android::GetJString(env, jpath), FileSys::OpenMode::Read); file) {
|
||||
if (auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file); loader) {
|
||||
auto const file_type = loader->GetFileType();
|
||||
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error)
|
||||
return false;
|
||||
if ((file_type == Loader::FileType::NSP || file_type == Loader::FileType::XCI) && !Loader::IsBootableGameContainer(file, file_type))
|
||||
return false;
|
||||
u64 program_id = 0;
|
||||
return loader->ReadProgramId(program_id) == Loader::ResultStatus::Success;
|
||||
}
|
||||
}
|
||||
|
||||
auto loader = Loader::GetLoader(EmulationSession::GetInstance().System(), file);
|
||||
if (!loader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto file_type = loader->GetFileType();
|
||||
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((file_type == Loader::FileType::NSP || file_type == Loader::FileType::XCI) &&
|
||||
!Loader::IsBootableGameContainer(file, file_type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 program_id = 0;
|
||||
Loader::ResultStatus res = loader->ReadProgramId(program_id);
|
||||
if (res != Loader::ResultStatus::Success) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
return Common::Android::ToJString(
|
||||
env, GetRomMetadata(Common::Android::GetJString(env, jpath)).title);
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getTitle(JNIEnv* env, jobject obj, jstring jpath) {
|
||||
return Common::Android::ToJString(env, GetRomMetadata(Common::Android::GetJString(env, jpath)).title);
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
return Common::Android::ToJString(
|
||||
env, std::to_string(GetRomMetadata(Common::Android::GetJString(env, jpath)).programId));
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getProgramId(JNIEnv* env, jobject obj, jstring jpath) {
|
||||
return Common::Android::ToJString(env, std::to_string(GetRomMetadata(Common::Android::GetJString(env, jpath)).programId));
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
return Common::Android::ToJString(
|
||||
env, GetRomMetadata(Common::Android::GetJString(env, jpath)).developer);
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getDeveloper(JNIEnv* env, jobject obj, jstring jpath) {
|
||||
return Common::Android::ToJString(env, GetRomMetadata(Common::Android::GetJString(env, jpath)).developer);
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj,
|
||||
jstring jpath, jboolean jreload) {
|
||||
return Common::Android::ToJString(
|
||||
env, GetRomMetadata(Common::Android::GetJString(env, jpath), jreload).version);
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getVersion(JNIEnv* env, jobject obj, jstring jpath, jboolean jreload) {
|
||||
return Common::Android::ToJString(env, GetRomMetadata(Common::Android::GetJString(env, jpath), jreload).version);
|
||||
}
|
||||
|
||||
jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
jbyteArray Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIcon(JNIEnv* env, jobject obj, jstring jpath) {
|
||||
auto icon_data = GetRomMetadata(Common::Android::GetJString(env, jpath)).icon;
|
||||
jbyteArray icon = env->NewByteArray(static_cast<jsize>(icon_data.size()));
|
||||
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon),
|
||||
reinterpret_cast<jbyte*>(icon_data.data()));
|
||||
jbyteArray icon = env->NewByteArray(jsize(icon_data.size()));
|
||||
env->SetByteArrayRegion(icon, 0, env->GetArrayLength(icon), reinterpret_cast<jbyte*>(icon_data.data()));
|
||||
return icon;
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj,
|
||||
jstring jpath) {
|
||||
return static_cast<jboolean>(
|
||||
GetRomMetadata(Common::Android::GetJString(env, jpath)).isHomebrew);
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsHomebrew(JNIEnv* env, jobject obj, jstring jpath) {
|
||||
return jboolean(GetRomMetadata(Common::Android::GetJString(env, jpath)).isHomebrew);
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_GameMetadata_resetMetadata(JNIEnv* env, jobject obj) {
|
||||
|
||||
@@ -325,6 +325,9 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
||||
m_system.GetCpuManager().OnGpuReady();
|
||||
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||
|
||||
m_system.RegisterApplicationChangedCallback(
|
||||
[&](u64 changed_program_id) { RequestDiskShaderCacheReload(changed_program_id); });
|
||||
|
||||
// Register an ExecuteProgram callback such that Core can execute a sub-program
|
||||
m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) {
|
||||
m_next_program_index = program_index_;
|
||||
@@ -404,20 +407,58 @@ void EmulationSession::RunEmulation() {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
std::optional<u64> reload_title;
|
||||
{
|
||||
[[maybe_unused]] std::unique_lock lock(m_mutex);
|
||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800),
|
||||
[&]() { return !m_is_running; })) {
|
||||
// Emulation halted.
|
||||
break;
|
||||
if (m_cv.wait_for(lock, std::chrono::milliseconds(800), [&]() {
|
||||
return !m_is_running || m_pending_shader_cache_title.has_value();
|
||||
})) {
|
||||
if (!m_is_running) {
|
||||
break;
|
||||
}
|
||||
reload_title = std::exchange(m_pending_shader_cache_title, std::nullopt);
|
||||
}
|
||||
}
|
||||
|
||||
if (reload_title.has_value())
|
||||
ReloadDiskShaderCache(*reload_title);
|
||||
}
|
||||
|
||||
// Reset current applet ID.
|
||||
m_applet_id = static_cast<int>(Service::AM::AppletId::Application);
|
||||
}
|
||||
|
||||
void EmulationSession::RequestDiskShaderCacheReload(u64 program_id) {
|
||||
{
|
||||
std::scoped_lock lock(m_mutex);
|
||||
m_pending_shader_cache_title = program_id;
|
||||
}
|
||||
m_cv.notify_one();
|
||||
}
|
||||
|
||||
void EmulationSession::ReloadDiskShaderCache(u64 program_id) {
|
||||
if (!Settings::values.use_disk_shader_cache.GetValue())
|
||||
return;
|
||||
|
||||
LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", program_id);
|
||||
|
||||
const bool was_paused = m_is_paused;
|
||||
|
||||
m_system.Pause();
|
||||
m_system.GPU().WaitForIdle();
|
||||
m_system.GPU().ObtainContext();
|
||||
|
||||
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
m_system.Renderer().ReadRasterizer()->LoadDiskResources(program_id, std::stop_token{},
|
||||
LoadDiskCacheProgress);
|
||||
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||
|
||||
m_system.GPU().ReleaseContext();
|
||||
|
||||
if (!was_paused)
|
||||
m_system.Run();
|
||||
}
|
||||
|
||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() {
|
||||
return m_software_keyboard;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <android/native_window_jni.h>
|
||||
#include "common/android/applets/software_keyboard.h"
|
||||
#include "core/core.h"
|
||||
@@ -44,6 +46,7 @@ public:
|
||||
void HaltEmulation();
|
||||
void RunEmulation();
|
||||
void ShutdownEmulation();
|
||||
void RequestDiskShaderCacheReload(u64 program_id);
|
||||
|
||||
const Core::PerfStatsResults& PerfStats();
|
||||
int ShadersBuilding();
|
||||
@@ -65,6 +68,7 @@ private:
|
||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||
static void OnEmulationStopped(Core::SystemResultStatus result);
|
||||
static void ChangeProgram(std::size_t program_index);
|
||||
void ReloadDiskShaderCache(u64 program_id);
|
||||
|
||||
private:
|
||||
// Window management
|
||||
@@ -83,6 +87,7 @@ private:
|
||||
Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||
int m_applet_id{1};
|
||||
std::optional<u64> m_pending_shader_cache_title;
|
||||
|
||||
// GPU driver parameters
|
||||
std::shared_ptr<Common::DynamicLibrary> m_vulkan_library;
|
||||
|
||||
+20
-139
@@ -1,6 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2013 Dolphin Emulator Project
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
@@ -40,110 +39,6 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <sys/resource.h>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_URGENT_AUDIO = -19;
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_AUDIO = -16;
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_URGENT_DISPLAY = -8;
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_DISPLAY = -4;
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_FOREGROUND = -2;
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_MORE_FAVORABLE = -1;
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_DEFAULT = 0;
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_LESS_FAVORABLE = 1;
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_BACKGROUND = 10;
|
||||
[[maybe_unused]] constexpr int ANDROID_THREAD_PRIORITY_LOWEST = 19;
|
||||
|
||||
constexpr size_t ANDROID_MINIMUM_PERFORMANCE_CORES = 4;
|
||||
|
||||
cpu_set_t ComputePerformanceCoreMask() {
|
||||
cpu_set_t mask;
|
||||
CPU_ZERO(&mask);
|
||||
|
||||
cpu_set_t allowed;
|
||||
CPU_ZERO(&allowed);
|
||||
if (sched_getaffinity(gettid(), sizeof(allowed), &allowed) != 0) {
|
||||
return mask;
|
||||
}
|
||||
|
||||
std::vector<std::pair<long, int>> cores;
|
||||
const int total = static_cast<int>(std::thread::hardware_concurrency());
|
||||
for (int cpu = 0; cpu < total; ++cpu) {
|
||||
if (!CPU_ISSET(cpu, &allowed)) {
|
||||
continue;
|
||||
}
|
||||
long max_frequency = 0;
|
||||
std::ifstream file("/sys/devices/system/cpu/cpu" + std::to_string(cpu) +
|
||||
"/cpufreq/cpuinfo_max_freq");
|
||||
if (!file || !(file >> max_frequency) || max_frequency <= 0) {
|
||||
CPU_ZERO(&mask);
|
||||
return mask;
|
||||
}
|
||||
cores.emplace_back(max_frequency, cpu);
|
||||
}
|
||||
if (cores.empty()) {
|
||||
return mask;
|
||||
}
|
||||
|
||||
std::sort(cores.begin(), cores.end(),
|
||||
[](const auto& lhs, const auto& rhs) { return lhs.first > rhs.first; });
|
||||
|
||||
size_t taken = 0;
|
||||
long cluster_frequency = cores.front().first;
|
||||
for (const auto& [frequency, cpu] : cores) {
|
||||
if (frequency != cluster_frequency) {
|
||||
if (taken >= ANDROID_MINIMUM_PERFORMANCE_CORES) {
|
||||
break;
|
||||
}
|
||||
cluster_frequency = frequency;
|
||||
}
|
||||
CPU_SET(cpu, &mask);
|
||||
++taken;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
const cpu_set_t& PerformanceCoreMask() {
|
||||
static const cpu_set_t mask = ComputePerformanceCoreMask();
|
||||
return mask;
|
||||
}
|
||||
|
||||
cpu_set_t ComputeEfficiencyCoreMask() {
|
||||
cpu_set_t mask;
|
||||
CPU_ZERO(&mask);
|
||||
|
||||
const cpu_set_t& performance = PerformanceCoreMask();
|
||||
if (CPU_COUNT(&performance) == 0) {
|
||||
return mask;
|
||||
}
|
||||
|
||||
cpu_set_t allowed;
|
||||
CPU_ZERO(&allowed);
|
||||
if (sched_getaffinity(gettid(), sizeof(allowed), &allowed) != 0) {
|
||||
return mask;
|
||||
}
|
||||
|
||||
const int total = static_cast<int>(std::thread::hardware_concurrency());
|
||||
for (int cpu = 0; cpu < total; ++cpu) {
|
||||
if (CPU_ISSET(cpu, &allowed) && !CPU_ISSET(cpu, &performance)) {
|
||||
CPU_SET(cpu, &mask);
|
||||
}
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
const cpu_set_t& EfficiencyCoreMask() {
|
||||
static const cpu_set_t mask = ComputeEfficiencyCoreMask();
|
||||
return mask;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
#endif
|
||||
|
||||
#include "common/cpu_features.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#ifdef _MSC_VER
|
||||
@@ -183,21 +78,6 @@ void SetCurrentThreadPriority(ThreadPriority new_priority) {
|
||||
}
|
||||
}();
|
||||
set_thread_priority(find_thread(NULL), priority);
|
||||
#elif defined(__ANDROID__)
|
||||
const int nice_value = [&]() {
|
||||
switch (new_priority) {
|
||||
case ThreadPriority::Low: return ANDROID_THREAD_PRIORITY_BACKGROUND;
|
||||
case ThreadPriority::Normal: return ANDROID_THREAD_PRIORITY_DEFAULT;
|
||||
case ThreadPriority::High: return ANDROID_THREAD_PRIORITY_DISPLAY;
|
||||
case ThreadPriority::VeryHigh: return ANDROID_THREAD_PRIORITY_URGENT_DISPLAY;
|
||||
case ThreadPriority::Critical: return ANDROID_THREAD_PRIORITY_AUDIO;
|
||||
default: return ANDROID_THREAD_PRIORITY_DEFAULT;
|
||||
}
|
||||
}();
|
||||
if (setpriority(PRIO_PROCESS, static_cast<id_t>(gettid()), nice_value) != 0) {
|
||||
LOG_DEBUG(Common, "Could not set thread nice value to {}: {}", nice_value,
|
||||
GetLastErrorMsg());
|
||||
}
|
||||
#else
|
||||
pthread_t this_thread = pthread_self();
|
||||
const auto scheduling_type = SCHED_OTHER;
|
||||
@@ -252,28 +132,29 @@ void SetCurrentThreadName(const char* name) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetCurrentThreadToPerformanceCores() {
|
||||
void PinCurrentThreadToPerformanceCore(size_t core_id) {
|
||||
ASSERT(core_id < 4);
|
||||
// If we set a flag for a CPU that doesn't exist, the thread may not be allowed to
|
||||
// run in ANY processor!
|
||||
auto const total_cores = std::thread::hardware_concurrency();
|
||||
if (core_id < total_cores) {
|
||||
#if defined(__ANDROID__)
|
||||
const cpu_set_t& mask = PerformanceCoreMask();
|
||||
if (CPU_COUNT(&mask) == 0) {
|
||||
return;
|
||||
}
|
||||
if (sched_setaffinity(gettid(), sizeof(mask), &mask) != 0) {
|
||||
LOG_DEBUG(Common, "Could not restrict thread to performance cores: {}", GetLastErrorMsg());
|
||||
}
|
||||
cpu_set_t set;
|
||||
CPU_ZERO(&set);
|
||||
CPU_SET(core_id, &set);
|
||||
sched_setaffinity(pthread_self(), sizeof(set), &set);
|
||||
#elif defined(__linux__) || defined(__FreeBSD__)
|
||||
cpu_set_t set;
|
||||
CPU_ZERO(&set);
|
||||
CPU_SET(core_id, &set);
|
||||
pthread_setaffinity_np(pthread_self(), sizeof(set), &set);
|
||||
#elif defined(_WIN32)
|
||||
DWORD set = 1UL << core_id;
|
||||
SetThreadAffinityMask(GetCurrentThread(), set);
|
||||
#else
|
||||
// No pin functionality implemented
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetCurrentThreadToEfficiencyCores() {
|
||||
#if defined(__ANDROID__)
|
||||
const cpu_set_t& mask = EfficiencyCoreMask();
|
||||
if (CPU_COUNT(&mask) == 0) {
|
||||
return;
|
||||
}
|
||||
if (sched_setaffinity(gettid(), sizeof(mask), &mask) != 0) {
|
||||
LOG_DEBUG(Common, "Could not restrict thread to efficiency cores: {}", GetLastErrorMsg());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
|
||||
+1
-7
@@ -99,14 +99,8 @@ enum class ThreadPriority : u32 {
|
||||
Critical = 4,
|
||||
};
|
||||
|
||||
enum class ThreadPlacement : u32 {
|
||||
Default = 0,
|
||||
Background = 1,
|
||||
};
|
||||
|
||||
void SetCurrentThreadPriority(ThreadPriority new_priority);
|
||||
void SetCurrentThreadName(const char* name);
|
||||
void SetCurrentThreadToPerformanceCores();
|
||||
void SetCurrentThreadToEfficiencyCores();
|
||||
void PinCurrentThreadToPerformanceCore(size_t core_id);
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -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 2020 yuzu Emulator Project
|
||||
@@ -37,15 +37,10 @@ class StatefulThreadWorker {
|
||||
using StateMaker = std::conditional_t<with_state, std::function<StateType()>, DummyCallable>;
|
||||
|
||||
public:
|
||||
explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {},
|
||||
ThreadPlacement placement = ThreadPlacement::Default)
|
||||
explicit StatefulThreadWorker(size_t num_workers, std::string name, StateMaker func = {})
|
||||
: workers_queued{num_workers}, thread_name{std::move(name)} {
|
||||
const auto lambda = [this, func, placement](std::stop_token stop_token) {
|
||||
const auto lambda = [this, func](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName(thread_name.c_str());
|
||||
if (placement == ThreadPlacement::Background) {
|
||||
Common::SetCurrentThreadPriority(ThreadPriority::Low);
|
||||
Common::SetCurrentThreadToEfficiencyCores();
|
||||
}
|
||||
{
|
||||
[[maybe_unused]] std::conditional_t<with_state, StateType, int> state{func()};
|
||||
while (!stop_token.stop_requested()) {
|
||||
|
||||
@@ -208,4 +208,12 @@ UUID UUID::MakeRandomRFC4122V4() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
UUID UUID::MakeRFC4122V5(std::span<u8, 20> sha1) {
|
||||
UUID uuid{};
|
||||
std::memcpy(&uuid.uuid, sha1.data(), sizeof(UUID));
|
||||
uuid.uuid[8] = 0x80 | (uuid.uuid[8] & 0x3F);
|
||||
uuid.uuid[6] = 0x50 | (uuid.uuid[6] & 0xF);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+16
-20
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -5,6 +8,7 @@
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
@@ -86,28 +90,20 @@ struct UUID {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a random UUID.
|
||||
*
|
||||
* @returns A random UUID.
|
||||
*/
|
||||
static UUID MakeRandom();
|
||||
/// @brief Creates a random UUID.
|
||||
/// @returns A random UUID.
|
||||
[[nodiscard]] static UUID MakeRandom();
|
||||
|
||||
/**
|
||||
* Creates a random UUID with a seed.
|
||||
*
|
||||
* @param seed A seed to initialize the Mersenne-Twister RNG
|
||||
*
|
||||
* @returns A random UUID.
|
||||
*/
|
||||
static UUID MakeRandomWithSeed(u32 seed);
|
||||
/// @brief Creates a random UUID with a seed.
|
||||
/// @param seed A seed to initialize the Mersenne-Twister RNG
|
||||
/// @returns A random UUID.
|
||||
[[nodiscard]] static UUID MakeRandomWithSeed(u32 seed);
|
||||
|
||||
/**
|
||||
* Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
|
||||
*
|
||||
* @returns A random UUID that is RFC 4122 Version 4 compliant.
|
||||
*/
|
||||
static UUID MakeRandomRFC4122V4();
|
||||
/// @brief Creates a random UUID. The generated UUID is RFC 4122 Version 4 compliant.
|
||||
/// @returns A random UUID that is RFC 4122 Version 4 compliant.
|
||||
[[nodiscard]] static UUID MakeRandomRFC4122V4();
|
||||
|
||||
[[nodiscard]] static UUID MakeRFC4122V5(std::span<u8, 20> sha1);
|
||||
|
||||
friend constexpr bool operator==(const UUID& lhs, const UUID& rhs) = default;
|
||||
};
|
||||
|
||||
+33
-2
@@ -329,7 +329,7 @@ struct System::Impl {
|
||||
LaunchTimestampCache::SaveLaunchTimestamp(params.program_id);
|
||||
|
||||
// Make the process created be the application
|
||||
kernel.MakeApplicationProcess(process->GetHandle());
|
||||
kernel.SetApplicationProcess(process->GetHandle());
|
||||
|
||||
// Set up the rest of the system.
|
||||
SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)};
|
||||
@@ -465,6 +465,7 @@ struct System::Impl {
|
||||
Core::SpeedLimiter speed_limiter;
|
||||
ExecuteProgramCallback execute_program_callback;
|
||||
ExitCallback exit_callback;
|
||||
ApplicationChangedCallback application_changed_callback;
|
||||
|
||||
std::optional<Service::Services> services;
|
||||
std::optional<Core::Debugger> debugger;
|
||||
@@ -725,7 +726,25 @@ const Core::SpeedLimiter& System::SpeedLimiter() const {
|
||||
}
|
||||
|
||||
u64 System::GetApplicationProcessProgramID() const {
|
||||
return impl->kernel.ApplicationProcess()->GetProgramId();
|
||||
const auto* const process = impl->kernel.ApplicationProcess();
|
||||
return process != nullptr ? process->GetProgramId() : 0;
|
||||
}
|
||||
|
||||
u64 System::GetProgramIdForProcessId(u64 process_id) const {
|
||||
auto process = impl->kernel.GetProcessByProcessId(process_id);
|
||||
return process.IsNull() ? 0 : process->GetProgramId();
|
||||
}
|
||||
|
||||
u64 System::ResolveCallerProgramId(u64 process_id) const {
|
||||
if (const auto program_id = this->GetProgramIdForProcessId(process_id); program_id != 0) {
|
||||
return program_id;
|
||||
}
|
||||
|
||||
const auto fallback = this->GetApplicationProcessProgramID();
|
||||
LOG_WARNING(Core,
|
||||
"Could not resolve caller process_id={}, falling back to application {:016X}",
|
||||
process_id, fallback);
|
||||
return fallback;
|
||||
}
|
||||
|
||||
Loader::ResultStatus System::GetGameName(std::string& out) const {
|
||||
@@ -959,6 +978,18 @@ void System::Exit() {
|
||||
}
|
||||
}
|
||||
|
||||
void System::RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback) {
|
||||
impl->application_changed_callback = std::move(callback);
|
||||
}
|
||||
|
||||
void System::NotifyApplicationChanged(u64 program_id) {
|
||||
//LOG_DEBUG(Core, "Running application changed to {:016X}", program_id);
|
||||
|
||||
if (impl->application_changed_callback) {
|
||||
impl->application_changed_callback(program_id);
|
||||
}
|
||||
}
|
||||
|
||||
void System::ApplySettings() {
|
||||
impl->RefreshTime(*this);
|
||||
|
||||
|
||||
@@ -322,6 +322,10 @@ public:
|
||||
|
||||
[[nodiscard]] u64 GetApplicationProcessProgramID() const;
|
||||
|
||||
[[nodiscard]] u64 GetProgramIdForProcessId(u64 process_id) const;
|
||||
|
||||
[[nodiscard]] u64 ResolveCallerProgramId(u64 process_id) const;
|
||||
|
||||
/// Gets the name of the current game
|
||||
[[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const;
|
||||
|
||||
@@ -435,6 +439,10 @@ public:
|
||||
/// Instructs the frontend to exit the application.
|
||||
void Exit();
|
||||
|
||||
using ApplicationChangedCallback = std::function<void(u64 program_id)>;
|
||||
void RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback);
|
||||
void NotifyApplicationChanged(u64 program_id);
|
||||
|
||||
/// Applies any changes to settings to this core instance.
|
||||
void ApplySettings();
|
||||
|
||||
|
||||
@@ -174,7 +174,12 @@ void CpuManager::RunThread(std::stop_token token, std::size_t core) {
|
||||
std::string name = is_multicore ? ("CPUCore_" + std::to_string(core)) : std::string{"CPUThread"};
|
||||
Common::SetCurrentThreadName(name.c_str());
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||
Common::SetCurrentThreadToPerformanceCores();
|
||||
#ifdef __ANDROID__
|
||||
// Aimed specifically for Snapdragon 8 Elite devices
|
||||
// This kills performance on desktop, but boosts perf for UMA devices
|
||||
// like the S8E. Mediatek and Mali likely won't suffer.
|
||||
Common::PinCurrentThreadToPerformanceCore(core);
|
||||
#endif
|
||||
auto& data = core_data[core];
|
||||
data.host_context = Common::Fiber::ThreadToFiber();
|
||||
|
||||
|
||||
@@ -274,23 +274,22 @@ public:
|
||||
explicit NACP(VirtualFile file);
|
||||
~NACP();
|
||||
|
||||
const LanguageEntry& GetLanguageEntry() const;
|
||||
std::string GetApplicationName() const;
|
||||
std::string GetDeveloperName() const;
|
||||
u64 GetTitleId() const;
|
||||
u64 GetDLCBaseTitleId() const;
|
||||
std::string GetVersionString() const;
|
||||
u64 GetDefaultNormalSaveSize() const;
|
||||
u64 GetDefaultJournalSaveSize() const;
|
||||
u32 GetSupportedLanguages() const;
|
||||
std::vector<std::string> GetApplicationNames() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
bool GetUserAccountSwitchLock() const;
|
||||
u64 GetDeviceSaveDataSize() const;
|
||||
u32 GetParentalControlFlag() const;
|
||||
const std::array<u8, 0x20>& GetRatingAge() const;
|
||||
[[nodiscard]] const LanguageEntry& GetLanguageEntry() const;
|
||||
[[nodiscard]] std::string GetApplicationName() const;
|
||||
[[nodiscard]] std::string GetDeveloperName() const;
|
||||
[[nodiscard]] u64 GetTitleId() const;
|
||||
[[nodiscard]] u64 GetDLCBaseTitleId() const;
|
||||
[[nodiscard]] std::string GetVersionString() const;
|
||||
[[nodiscard]] u64 GetDefaultNormalSaveSize() const;
|
||||
[[nodiscard]] u64 GetDefaultJournalSaveSize() const;
|
||||
[[nodiscard]] u32 GetSupportedLanguages() const;
|
||||
[[nodiscard]] std::vector<std::string> GetApplicationNames() const;
|
||||
[[nodiscard]] std::vector<u8> GetRawBytes() const;
|
||||
[[nodiscard]] bool GetUserAccountSwitchLock() const;
|
||||
[[nodiscard]] u64 GetDeviceSaveDataSize() const;
|
||||
[[nodiscard]] u32 GetParentalControlFlag() const;
|
||||
[[nodiscard]] const std::array<u8, 0x20>& GetRatingAge() const;
|
||||
|
||||
private:
|
||||
RawNACP raw{};
|
||||
std::vector<LanguageEntry> language_entries;
|
||||
};
|
||||
|
||||
@@ -1156,4 +1156,14 @@ PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
|
||||
|
||||
return {std::move(nacp), icon_file};
|
||||
}
|
||||
|
||||
[[nodiscard]] PatchManager::Metadata PatchManager::GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept {
|
||||
const FileSys::PatchManager pm{application_id, system.GetFileSystemController(), system.GetContentProvider()};
|
||||
auto metadata = pm.GetControlMetadata();
|
||||
if (metadata.first != nullptr)
|
||||
return metadata;
|
||||
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(application_id), system.GetFileSystemController(), system.GetContentProvider()};
|
||||
return pm_update.GetControlMetadata();
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
||||
@@ -105,6 +105,9 @@ public:
|
||||
// Version of GetControlMetadata that takes an arbitrary NCA
|
||||
[[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const;
|
||||
|
||||
/// @brief Gets NACP metadata (accounting for any patches or updates)
|
||||
[[nodiscard]] static PatchManager::Metadata GetMetadataFromBaseOrUpdate(Core::System& system, u64 application_id) noexcept;
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
|
||||
const std::string& build_id) const;
|
||||
|
||||
@@ -349,9 +349,18 @@ struct KernelCore::Impl {
|
||||
object_name_global_data.emplace(kernel);
|
||||
}
|
||||
|
||||
void MakeApplicationProcess(KernelCore& kernel, KProcess* process) {
|
||||
void SetApplicationProcess(KernelCore& kernel, KProcess* process) {
|
||||
if (application_process == process)
|
||||
return;
|
||||
|
||||
KProcess* const previous = application_process;
|
||||
application_process = process;
|
||||
application_process->Open(kernel);
|
||||
|
||||
if (application_process != nullptr)
|
||||
application_process->Open(kernel);
|
||||
|
||||
if (previous != nullptr)
|
||||
previous->Close(kernel);
|
||||
}
|
||||
|
||||
/// Sets the host thread ID for the caller.
|
||||
@@ -879,8 +888,8 @@ void KernelCore::RemoveProcess(KProcess* process) {
|
||||
}
|
||||
}
|
||||
|
||||
void KernelCore::MakeApplicationProcess(KProcess* process) {
|
||||
impl->MakeApplicationProcess(*this, process);
|
||||
void KernelCore::SetApplicationProcess(KProcess* process) {
|
||||
impl->SetApplicationProcess(*this, process);
|
||||
}
|
||||
|
||||
KProcess* KernelCore::ApplicationProcess() {
|
||||
@@ -891,6 +900,14 @@ const KProcess* KernelCore::ApplicationProcess() const {
|
||||
return impl->application_process;
|
||||
}
|
||||
|
||||
KScopedAutoObject<KProcess> KernelCore::GetProcessByProcessId(u64 process_id) {
|
||||
std::scoped_lock lk{impl->process_list_lock};
|
||||
for (auto* const process : impl->process_list)
|
||||
if (process != nullptr && process->GetProcessId() == process_id)
|
||||
return {*this, process};
|
||||
return {*this, nullptr};
|
||||
}
|
||||
|
||||
std::list<KScopedAutoObject<KProcess>> KernelCore::GetProcessList() {
|
||||
std::list<KScopedAutoObject<KProcess>> processes;
|
||||
std::scoped_lock lk{impl->process_list_lock};
|
||||
|
||||
@@ -124,8 +124,8 @@ public:
|
||||
void AppendNewProcess(KProcess* process);
|
||||
void RemoveProcess(KProcess* process);
|
||||
|
||||
/// Makes the given process the new application process.
|
||||
void MakeApplicationProcess(KProcess* process);
|
||||
/// Makes the given process the current application process.
|
||||
void SetApplicationProcess(KProcess* process);
|
||||
|
||||
/// Retrieves a pointer to the application process.
|
||||
KProcess* ApplicationProcess();
|
||||
@@ -133,6 +133,9 @@ public:
|
||||
/// Retrieves a const pointer to the application process.
|
||||
const KProcess* ApplicationProcess() const;
|
||||
|
||||
/// Retrieves the process with the given process ID, or a null object.
|
||||
KScopedAutoObject<KProcess> GetProcessByProcessId(u64 process_id);
|
||||
|
||||
/// Retrieves the list of processes.
|
||||
std::list<KScopedAutoObject<KProcess>> GetProcessList();
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -10,6 +13,7 @@ namespace Service::AM {
|
||||
constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
|
||||
constexpr Result ResultNoMessages{ErrorModule::AM, 3};
|
||||
constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22};
|
||||
constexpr Result ResultApplicationRecordNotFound{ErrorModule::AM, 37};
|
||||
constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
|
||||
constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511};
|
||||
constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -201,6 +201,14 @@ enum class ProgramSpecifyKind : u32 {
|
||||
RestartProgram = 2,
|
||||
};
|
||||
|
||||
// Maufeat: Use enums for zindex instead of using random zindex numbers
|
||||
enum AppletZIndex : s32 {
|
||||
Background = 0,
|
||||
Foreground = 1,
|
||||
ForegroundVisible = 2,
|
||||
Overlay = 3,
|
||||
};
|
||||
|
||||
struct CommonArguments {
|
||||
CommonArgumentVersion arguments_version;
|
||||
CommonArgumentSize size;
|
||||
|
||||
@@ -56,22 +56,22 @@ void Applet::UpdateSuspensionStateLocked(bool force_message) {
|
||||
}
|
||||
}
|
||||
|
||||
void Applet::SetInteractibleLocked(bool interactible) {
|
||||
if (is_interactible == interactible) {
|
||||
void Applet::SetInteractibleLocked(bool pad_interactible, bool touch_interactible) {
|
||||
if (is_pad_interactible == pad_interactible && is_touch_interactible == touch_interactible) {
|
||||
return;
|
||||
}
|
||||
|
||||
is_interactible = interactible;
|
||||
is_pad_interactible = pad_interactible;
|
||||
is_touch_interactible = touch_interactible;
|
||||
|
||||
const bool exit_requested = lifecycle_manager.GetExitRequested();
|
||||
const bool input_enabled = interactible && !exit_requested;
|
||||
const bool pad_enabled = pad_interactible && !exit_requested;
|
||||
const bool touch_enabled = touch_interactible && !exit_requested;
|
||||
|
||||
if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) {
|
||||
LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}",
|
||||
static_cast<u32>(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground);
|
||||
}
|
||||
LOG_DEBUG(Service_AM, "applet={} pad={} touch={} exit_requested={}",
|
||||
static_cast<u32>(applet_id), pad_enabled, touch_enabled, exit_requested);
|
||||
|
||||
hid_registration.EnableAppletToGetInput(input_enabled);
|
||||
hid_registration.EnableAppletToGetInput(pad_enabled, touch_enabled);
|
||||
}
|
||||
|
||||
void Applet::OnProcessTerminatedLocked() {
|
||||
|
||||
@@ -125,9 +125,11 @@ struct Applet {
|
||||
bool album_image_taken_notification_enabled{};
|
||||
bool record_volume_muted{};
|
||||
bool is_activity_runnable{};
|
||||
bool is_interactible{true};
|
||||
bool is_pad_interactible{true};
|
||||
bool is_touch_interactible{true};
|
||||
bool window_visible{true};
|
||||
bool overlay_in_foreground{false};
|
||||
bool overlay_watching_short_home_button{false};
|
||||
bool overlay_handling_touch_input{false};
|
||||
|
||||
// Events
|
||||
Event overlay_event;
|
||||
@@ -148,7 +150,7 @@ struct Applet {
|
||||
|
||||
// Process state management
|
||||
void UpdateSuspensionStateLocked(bool force_message);
|
||||
void SetInteractibleLocked(bool interactible);
|
||||
void SetInteractibleLocked(bool pad_interactible, bool touch_interactible);
|
||||
void OnProcessTerminatedLocked();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/am/display_layer_manager.h"
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/hle/service/vi/application_display_service.h"
|
||||
#include "core/hle/service/vi/container.h"
|
||||
@@ -33,6 +34,7 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
|
||||
m_system_shared_buffer_id = 0;
|
||||
m_system_shared_layer_id = 0;
|
||||
m_applet_id = applet_id;
|
||||
m_library_applet_mode = mode;
|
||||
m_buffer_sharing_enabled = false;
|
||||
m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
|
||||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
|
||||
@@ -72,14 +74,16 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
|
||||
out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()}));
|
||||
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id,
|
||||
this->GetLayerStackMask());
|
||||
|
||||
if (m_applet_id != AppletId::Application) {
|
||||
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
|
||||
} else {
|
||||
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
|
||||
(void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,10 +126,12 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
|
||||
|
||||
// Ensure the overlay layer is visible
|
||||
m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id);
|
||||
(void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id,
|
||||
this->GetLayerStackMask());
|
||||
m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id);
|
||||
s32 initial_z = 1;
|
||||
s32 initial_z = Foreground;
|
||||
if (m_applet_id == AppletId::OverlayDisplay) {
|
||||
initial_z = -1;
|
||||
initial_z = Overlay;
|
||||
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
|
||||
}
|
||||
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
|
||||
@@ -142,6 +148,36 @@ Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_bu
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
u32 DisplayLayerManager::GetLayerStackMask() const {
|
||||
using Nvnflinger::LayerStackBit;
|
||||
using Nvnflinger::LayerStackId;
|
||||
|
||||
constexpr u32 Displayed = LayerStackBit(LayerStackId::Default);
|
||||
constexpr u32 Screenshot = LayerStackBit(LayerStackId::Screenshot);
|
||||
constexpr u32 Recording = LayerStackBit(LayerStackId::Recording);
|
||||
constexpr u32 LastFrame = LayerStackBit(LayerStackId::LastFrame);
|
||||
constexpr u32 Debug = LayerStackBit(LayerStackId::ApplicationForDebug);
|
||||
|
||||
switch (m_applet_id) {
|
||||
case AppletId::Application:
|
||||
return Displayed | Screenshot | Recording | LastFrame | Debug;
|
||||
case AppletId::OverlayDisplay:
|
||||
return Displayed;
|
||||
case AppletId::QLaunch:
|
||||
return Displayed;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_library_applet_mode) {
|
||||
case LibraryAppletMode::AllForeground:
|
||||
case LibraryAppletMode::AllForegroundInitiallyHidden:
|
||||
return Displayed | Screenshot | LastFrame;
|
||||
default:
|
||||
return Displayed | Screenshot;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayLayerManager::SetWindowVisibility(bool visible) {
|
||||
if (m_visible == visible) {
|
||||
return;
|
||||
@@ -185,10 +221,17 @@ void DisplayLayerManager::SetOverlayZIndex(s32 z_index) {
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written,
|
||||
s32* out_fbshare_layer_index) {
|
||||
s32* out_fbshare_layer_index,
|
||||
VI::CaptureKind kind) {
|
||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index));
|
||||
out_was_written, out_fbshare_layer_index, kind));
|
||||
}
|
||||
|
||||
Result DisplayLayerManager::ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color) {
|
||||
R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied);
|
||||
R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->ClearAppletCaptureBuffer(
|
||||
fbshare_layer_index, color));
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -23,6 +23,7 @@ class KProcess;
|
||||
namespace Service::VI {
|
||||
class IApplicationDisplayService;
|
||||
class IManagerDisplayService;
|
||||
enum class CaptureKind : u32;
|
||||
} // namespace Service::VI
|
||||
|
||||
namespace Service::AM {
|
||||
@@ -48,9 +49,13 @@ public:
|
||||
|
||||
void SetOverlayZIndex(s32 z_index);
|
||||
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index);
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index,
|
||||
VI::CaptureKind kind);
|
||||
Result ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color);
|
||||
|
||||
private:
|
||||
u32 GetLayerStackMask() const;
|
||||
|
||||
Kernel::KProcess* m_process{};
|
||||
std::shared_ptr<VI::IApplicationDisplayService> m_display_service{};
|
||||
std::shared_ptr<VI::IManagerDisplayService> m_manager_display_service{};
|
||||
@@ -59,6 +64,7 @@ private:
|
||||
u64 m_system_shared_buffer_id{};
|
||||
u64 m_system_shared_layer_id{};
|
||||
AppletId m_applet_id{};
|
||||
LibraryAppletMode m_library_applet_mode{};
|
||||
bool m_buffer_sharing_enabled{};
|
||||
bool m_blending_enabled{};
|
||||
bool m_visible{true};
|
||||
|
||||
@@ -36,12 +36,17 @@ HidRegistration::~HidRegistration() {
|
||||
}
|
||||
}
|
||||
|
||||
void HidRegistration::EnableAppletToGetInput(bool enable) {
|
||||
if (m_process.IsInitialized()) {
|
||||
m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(),
|
||||
enable);
|
||||
m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable);
|
||||
}
|
||||
void HidRegistration::EnableAppletToGetInput(bool enable_pad, bool enable_touch) {
|
||||
if (!m_process.IsInitialized())
|
||||
return;
|
||||
|
||||
const auto resource_manager = m_hid_server->GetResourceManager();
|
||||
const u64 aruid = m_process.GetProcessId();
|
||||
|
||||
resource_manager->EnablePadInput(aruid, enable_pad);
|
||||
resource_manager->EnableTouchScreen(aruid, enable_touch);
|
||||
|
||||
resource_manager->SetAruidValidForVibration(aruid, enable_pad);
|
||||
}
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
~HidRegistration();
|
||||
|
||||
void RegisterCurrentProcess();
|
||||
void EnableAppletToGetInput(bool enable);
|
||||
void EnableAppletToGetInput(bool enable_pad, bool enable_touch);
|
||||
|
||||
private:
|
||||
Process& m_process;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/common_funcs.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/service/applet_common_functions.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
@@ -78,7 +80,7 @@ Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) {
|
||||
|
||||
Result IAppletCommonFunctions::GetCurrentApplicationId(Out<u64> out_application_id) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL;
|
||||
*out_application_id = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <openssl/evp.h>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "common/uuid.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
@@ -154,20 +156,7 @@ Result IApplicationFunctions::GetDesiredLanguage(Out<u64> out_language_code) {
|
||||
// Default to 0 (all languages supported)
|
||||
u32 supported_languages = 0;
|
||||
|
||||
const auto res = [this] {
|
||||
const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
auto metadata = pm.GetControlMetadata();
|
||||
if (metadata.first != nullptr) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id),
|
||||
system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
return pm_update.GetControlMetadata();
|
||||
}();
|
||||
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||
if (res.first != nullptr) {
|
||||
supported_languages = res.first->GetSupportedLanguages();
|
||||
}
|
||||
@@ -205,20 +194,7 @@ Result IApplicationFunctions::SetTerminateResult(Result terminate_result) {
|
||||
Result IApplicationFunctions::GetDisplayVersion(Out<DisplayVersion> out_display_version) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
const auto res = [this] {
|
||||
const FileSys::PatchManager pm{m_applet->program_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
auto metadata = pm.GetControlMetadata();
|
||||
if (metadata.first != nullptr) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(m_applet->program_id),
|
||||
system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
return pm_update.GetControlMetadata();
|
||||
}();
|
||||
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||
if (res.first != nullptr) {
|
||||
const auto& version = res.first->GetVersionString();
|
||||
std::memcpy(out_display_version->string.data(), version.data(),
|
||||
@@ -347,8 +323,21 @@ Result IApplicationFunctions::NotifyRunning(Out<bool> out_became_running) {
|
||||
}
|
||||
|
||||
Result IApplicationFunctions::GetPseudoDeviceId(Out<Common::UUID> out_pseudo_device_id) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
*out_pseudo_device_id = {};
|
||||
LOG_WARNING(Service_AM, "(stubbed)");
|
||||
|
||||
// This should be hashed with the device specific hash
|
||||
// for now this will do
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id);
|
||||
u8 hash[EVP_MAX_MD_SIZE];
|
||||
unsigned int hash_len = 0;
|
||||
auto const seed = res.first->raw.seed_for_pseudo_device_id;
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_new();
|
||||
auto const algorithm = EVP_sha1();
|
||||
EVP_DigestInit_ex(ctx, algorithm, nullptr);
|
||||
EVP_DigestUpdate(ctx, &seed, sizeof(seed));
|
||||
EVP_DigestFinal_ex(ctx, hash, &hash_len);
|
||||
EVP_MD_CTX_free(ctx);
|
||||
*out_pseudo_device_id = Common::UUID::MakeRFC4122V5(std::span<u8, 20>{hash, std::size(hash)});
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/service/display_controller.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/vi/shared_buffer_manager.h"
|
||||
|
||||
namespace Service::AM {
|
||||
|
||||
@@ -71,16 +72,16 @@ Result IDisplayController::TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_l
|
||||
}
|
||||
|
||||
Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called, unknown0={} fbshare_layer_index={} color={:#x}",
|
||||
unknown0, fbshare_layer_index, color);
|
||||
R_SUCCEED();
|
||||
LOG_DEBUG(Service_AM, "called, unknown0={} fbshare_layer_index={} color={:#x}", unknown0,
|
||||
fbshare_layer_index, color);
|
||||
R_RETURN(applet->display_layer_manager.ClearAppletCaptureBuffer(fbshare_layer_index, color));
|
||||
}
|
||||
|
||||
Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer(
|
||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index));
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastForeground));
|
||||
}
|
||||
|
||||
Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
||||
@@ -90,9 +91,9 @@ Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() {
|
||||
|
||||
Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer(
|
||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index));
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index, VI::CaptureKind::CallerApplet));
|
||||
}
|
||||
|
||||
Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
||||
@@ -102,9 +103,9 @@ Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() {
|
||||
|
||||
Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer(
|
||||
Out<bool> out_was_written, Out<s32> out_fbshare_layer_index) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written,
|
||||
out_fbshare_layer_index));
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(
|
||||
out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastApplication));
|
||||
}
|
||||
|
||||
Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() {
|
||||
|
||||
@@ -252,19 +252,7 @@ Result ILibraryAppletSelfAccessor::GetMainAppletApplicationDesiredLanguage(
|
||||
// Default to 0 (all languages supported)
|
||||
u32 supported_languages = 0;
|
||||
|
||||
const auto res = [this, identity] {
|
||||
const FileSys::PatchManager pm{identity.application_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
auto metadata = pm.GetControlMetadata();
|
||||
if (metadata.first != nullptr) {
|
||||
return metadata;
|
||||
}
|
||||
|
||||
const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(identity.application_id),
|
||||
system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
return pm_update.GetControlMetadata();
|
||||
}();
|
||||
const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, identity.application_id);
|
||||
|
||||
if (res.first != nullptr) {
|
||||
supported_languages = res.first->GetSupportedLanguages();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/hle/service/am/applet.h"
|
||||
@@ -22,7 +22,7 @@ namespace Service::AM {
|
||||
{10, nullptr, "StartShutdownSequenceForOverlay"},
|
||||
{11, nullptr, "StartRebootSequenceForOverlay"},
|
||||
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
|
||||
{21, nullptr, "SetHandlingTouchScreenInputEnabled"},
|
||||
{21, D<&IOverlayFunctions::SetHandlingTouchScreenInputEnabled>, "SetHandlingTouchScreenInputEnabled"},
|
||||
{30, nullptr, "SetHealthWarningShowingState"},
|
||||
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
|
||||
{40, nullptr, "GetApplicationNintendoLogo"},
|
||||
@@ -43,10 +43,12 @@ namespace Service::AM {
|
||||
Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = true;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->overlay_watching_short_home_button = true;
|
||||
}
|
||||
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
@@ -56,16 +58,26 @@ namespace Service::AM {
|
||||
Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
m_applet->overlay_in_foreground = false;
|
||||
m_applet->home_button_short_pressed_blocked = false;
|
||||
{
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->overlay_watching_short_home_button = false;
|
||||
}
|
||||
|
||||
if (auto *window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
if (auto* window_system = system.GetAppletManager().GetWindowSystem()) {
|
||||
window_system->RequestUpdate();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::SetHandlingTouchScreenInputEnabled(bool enabled) {
|
||||
LOG_DEBUG(Service_AM, "called, enabled={}", enabled);
|
||||
|
||||
std::scoped_lock lk{m_applet->lock};
|
||||
m_applet->overlay_handling_touch_input = enabled;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
|
||||
LOG_DEBUG(Service_AM, "called");
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
@@ -20,6 +20,7 @@ namespace Service::AM {
|
||||
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
|
||||
Result IsHealthWarningRequired(Out<bool> is_required);
|
||||
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
|
||||
Result SetHandlingTouchScreenInputEnabled(bool enabled);
|
||||
Result Unknown70();
|
||||
|
||||
private:
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/settings.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/hle/service/am/applet.h"
|
||||
#include "core/hle/service/am/applet_manager.h"
|
||||
@@ -32,46 +36,82 @@ void WindowSystem::RequestUpdate() {
|
||||
}
|
||||
|
||||
void WindowSystem::Update() {
|
||||
std::scoped_lock lk{m_lock};
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||
LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}",
|
||||
m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr);
|
||||
|
||||
// Loop through all applets and remove terminated applets.
|
||||
this->PruneTerminatedAppletsLocked();
|
||||
// Loop through all applets and remove terminated applets.
|
||||
this->PruneTerminatedAppletsLocked();
|
||||
|
||||
// If the home menu is being locked into the foreground, handle that.
|
||||
if (this->LockHomeMenuIntoForegroundLocked()) {
|
||||
return;
|
||||
// If the home menu is being locked into the foreground, handle that.
|
||||
if (!this->LockHomeMenuIntoForegroundLocked()) {
|
||||
const bool overlay_takes_input = this->DoesOverlayTakeInputLocked();
|
||||
|
||||
this->UpdateAppletStateLocked(m_overlay_display, true, overlay_takes_input);
|
||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_takes_input);
|
||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_takes_input);
|
||||
}
|
||||
}
|
||||
|
||||
bool overlay_blocks_input = false;
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
overlay_blocks_input = m_overlay_display->overlay_in_foreground;
|
||||
}
|
||||
|
||||
// Recursively update each applet root.
|
||||
this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_blocks_input);
|
||||
this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_blocks_input);
|
||||
this->UpdateAppletStateLocked(m_overlay_display, true, false); // overlay is always updated, never blocked
|
||||
this->NotifyApplicationChangedIfNeeded();
|
||||
}
|
||||
|
||||
void WindowSystem::TrackApplet(std::shared_ptr<Applet> applet, bool is_application) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
m_overlay_display = applet.get();
|
||||
} else if (is_application) {
|
||||
ASSERT(m_application == nullptr);
|
||||
m_application = applet.get();
|
||||
if (applet->applet_id == AppletId::QLaunch) {
|
||||
ASSERT(m_home_menu == nullptr);
|
||||
m_home_menu = applet.get();
|
||||
} else if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
m_overlay_display = applet.get();
|
||||
} else if (is_application) {
|
||||
ASSERT(m_application == nullptr);
|
||||
m_application = applet.get();
|
||||
}
|
||||
|
||||
this->UpdateCurrentApplicationLocked();
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
||||
}
|
||||
|
||||
m_event_observer->TrackAppletProcess(*applet);
|
||||
m_applets.emplace(applet->aruid.pid, std::move(applet));
|
||||
this->NotifyApplicationChangedIfNeeded();
|
||||
}
|
||||
|
||||
void WindowSystem::UpdateCurrentApplicationLocked() {
|
||||
const Applet* const candidate = m_application != nullptr ? m_application : m_home_menu;
|
||||
if (candidate == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* const process = candidate->process->GetHandle();
|
||||
if (process == nullptr || process == m_system.Kernel().ApplicationProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(Service_AM, "Current application is now {:016X}", candidate->program_id);
|
||||
|
||||
m_system.Kernel().SetApplicationProcess(process);
|
||||
Settings::SetCurrentProgramID(candidate->program_id);
|
||||
|
||||
m_pending_application_notification = candidate->program_id;
|
||||
}
|
||||
|
||||
void WindowSystem::NotifyApplicationChangedIfNeeded() {
|
||||
std::optional<u64> program_id;
|
||||
{
|
||||
std::scoped_lock lk{m_lock};
|
||||
program_id = std::exchange(m_pending_application_notification, std::nullopt);
|
||||
}
|
||||
|
||||
if (!program_id.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_system.NotifyApplicationChanged(*program_id);
|
||||
}
|
||||
|
||||
std::shared_ptr<Applet> WindowSystem::GetByAppletResourceUserId(u64 aruid) {
|
||||
@@ -169,18 +209,40 @@ void WindowSystem::OnExitRequested() {
|
||||
}
|
||||
|
||||
void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) {
|
||||
if (m_home_menu) {
|
||||
std::scoped_lock lk_home{m_home_menu->lock};
|
||||
m_home_menu->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
}
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
m_overlay_display->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
}
|
||||
if (m_application) {
|
||||
std::scoped_lock lk_application{m_application->lock};
|
||||
m_application->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
}
|
||||
const auto is_blocked = [message](const Applet& applet) {
|
||||
if (message == AppletMessage::DetectShortPressingHomeButton &&
|
||||
applet.applet_id == AppletId::OverlayDisplay &&
|
||||
!applet.overlay_watching_short_home_button) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (message) {
|
||||
case AppletMessage::DetectShortPressingHomeButton:
|
||||
return applet.home_button_short_pressed_blocked;
|
||||
case AppletMessage::DetectLongPressingHomeButton:
|
||||
return applet.home_button_long_pressed_blocked;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const auto send_to = [&](Applet* applet) {
|
||||
if (!applet) {
|
||||
return;
|
||||
}
|
||||
std::scoped_lock lk{applet->lock};
|
||||
if (is_blocked(*applet)) {
|
||||
LOG_DEBUG(Service_AM, "Applet {} is blocking message {}",
|
||||
static_cast<u32>(applet->applet_id), static_cast<u32>(message));
|
||||
return;
|
||||
}
|
||||
applet->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message);
|
||||
};
|
||||
|
||||
send_to(m_home_menu);
|
||||
send_to(m_overlay_display);
|
||||
send_to(m_application);
|
||||
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
@@ -192,19 +254,9 @@ void WindowSystem::OnSystemButtonPress(SystemButtonType type) {
|
||||
case SystemButtonType::HomeButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton);
|
||||
break;
|
||||
case SystemButtonType::HomeButtonLongPressing: {
|
||||
// Toggle overlay foreground visibility on long home press
|
||||
if (m_overlay_display) {
|
||||
std::scoped_lock lk_overlay{m_overlay_display->lock};
|
||||
m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground;
|
||||
LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible);
|
||||
}
|
||||
case SystemButtonType::HomeButtonLongPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
|
||||
// Force a state update after toggling overlay
|
||||
if (m_event_observer) {
|
||||
m_event_observer->RequestUpdate();
|
||||
}
|
||||
break; }
|
||||
break;
|
||||
case SystemButtonType::CaptureButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton);
|
||||
break;
|
||||
@@ -317,6 +369,8 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
|
||||
m_overlay_display = nullptr;
|
||||
}
|
||||
|
||||
this->UpdateCurrentApplicationLocked();
|
||||
|
||||
// Finalize applet.
|
||||
applet->OnProcessTerminatedLocked();
|
||||
|
||||
@@ -389,7 +443,20 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) {
|
||||
applet->lock.lock();
|
||||
}
|
||||
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) {
|
||||
bool WindowSystem::IsOverlayOpenLocked(const Applet& overlay) const {
|
||||
return overlay.window_visible && overlay.overlay_watching_short_home_button;
|
||||
}
|
||||
|
||||
bool WindowSystem::DoesOverlayTakeInputLocked() const {
|
||||
if (m_overlay_display == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{m_overlay_display->lock};
|
||||
return this->IsOverlayOpenLocked(*m_overlay_display);
|
||||
}
|
||||
|
||||
void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input) {
|
||||
// With no applet, we don't have anything to do.
|
||||
if (!applet) {
|
||||
return;
|
||||
@@ -420,24 +487,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
return false;
|
||||
}();
|
||||
|
||||
const bool is_overlay = applet->applet_id == AppletId::OverlayDisplay;
|
||||
|
||||
// Update visibility state.
|
||||
// Overlay applets should always be visible when window_visible is true, regardless of foreground state
|
||||
const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? applet->window_visible
|
||||
: (is_foreground && applet->window_visible);
|
||||
const bool should_be_visible =
|
||||
is_overlay ? applet->window_visible : (is_foreground && applet->window_visible);
|
||||
applet->display_layer_manager.SetWindowVisibility(should_be_visible);
|
||||
|
||||
const bool needs_hid_input =
|
||||
is_overlay ? this->IsOverlayOpenLocked(*applet)
|
||||
: (is_foreground && applet->window_visible && !overlay_takes_input);
|
||||
|
||||
const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay)
|
||||
? applet->overlay_in_foreground
|
||||
: (is_foreground && applet->window_visible && !overlay_blocking);
|
||||
|
||||
if (applet->applet_id == AppletId::OverlayDisplay || applet->applet_id == AppletId::Application) {
|
||||
LOG_DEBUG(Service_AM, "UpdateAppletStateLocked: applet={} overlay_in_foreground={} is_foreground={} window_visible={} overlay_blocking={} should_be_interactible={}",
|
||||
static_cast<u32>(applet->applet_id), applet->overlay_in_foreground, is_foreground, applet->window_visible, overlay_blocking, should_be_interactible);
|
||||
}
|
||||
|
||||
applet->SetInteractibleLocked(should_be_interactible);
|
||||
applet->SetInteractibleLocked(needs_hid_input, needs_hid_input);
|
||||
|
||||
// Update focus state and suspension.
|
||||
const bool is_obscured = has_obscuring_child_applets || !applet->window_visible;
|
||||
@@ -453,23 +514,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
|
||||
applet->UpdateSuspensionStateLocked(true);
|
||||
}
|
||||
|
||||
// Z-index logic like in reference C# implementation (tuned for overlay extremes)
|
||||
s32 z_index = 0;
|
||||
const bool now_foreground = inherited_foreground;
|
||||
if (applet->applet_id == AppletId::OverlayDisplay) {
|
||||
z_index = applet->overlay_in_foreground ? 100000 : -1;
|
||||
} else if (now_foreground && !is_obscured) {
|
||||
z_index = 2;
|
||||
} else if (now_foreground) {
|
||||
z_index = 1;
|
||||
} else {
|
||||
z_index = 0;
|
||||
// Layer ordering. Composition sorts back-to-front. Now with enums for calrity.
|
||||
s32 z_index = Background;
|
||||
if (is_overlay) {
|
||||
z_index = Overlay;
|
||||
} else if (inherited_foreground) {
|
||||
z_index = is_obscured ? Foreground : ForegroundVisible;
|
||||
}
|
||||
applet->display_layer_manager.SetOverlayZIndex(z_index);
|
||||
|
||||
// Recurse into child applets.
|
||||
for (const auto& child_applet : applet->child_applets) {
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking);
|
||||
this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_takes_input);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/service/am/am_types.h"
|
||||
@@ -60,11 +61,16 @@ public:
|
||||
void OnPowerButtonPressed(ButtonPressDuration type) {}
|
||||
|
||||
private:
|
||||
void UpdateCurrentApplicationLocked();
|
||||
void NotifyApplicationChangedIfNeeded();
|
||||
void PruneTerminatedAppletsLocked();
|
||||
bool RestartAppletProcessLocked(Applet* applet);
|
||||
bool LockHomeMenuIntoForegroundLocked();
|
||||
void TerminateChildAppletsLocked(Applet* applet);
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
|
||||
bool IsOverlayOpenLocked(const Applet& overlay) const;
|
||||
bool DoesOverlayTakeInputLocked() const;
|
||||
|
||||
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input);
|
||||
void SendButtonAppletMessageLocked(AppletMessage message);
|
||||
|
||||
private:
|
||||
@@ -88,6 +94,7 @@ private:
|
||||
|
||||
// Applet map by aruid.
|
||||
std::map<u64, std::shared_ptr<Applet>> m_applets{};
|
||||
std::optional<u64> m_pending_application_notification{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/am/am_results.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::AOC {
|
||||
@@ -31,6 +32,10 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
|
||||
return FileSys::GetBaseTitleID(title_id) == base;
|
||||
}
|
||||
|
||||
static u64 GetCallerBaseTitleID(Core::System& system, const ClientProcessId& process_id) {
|
||||
return FileSys::GetBaseTitleID(system.ResolveCallerProgramId(*process_id));
|
||||
}
|
||||
|
||||
static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
|
||||
std::vector<u64> add_on_content;
|
||||
const auto& rcu = system.GetContentProvider();
|
||||
@@ -91,7 +96,7 @@ IAddOnContentManager::~IAddOnContentManager() {
|
||||
Result IAddOnContentManager::CountAddOnContent(Out<u32> out_count, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto current = system.GetApplicationProcessProgramID();
|
||||
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
|
||||
@@ -112,7 +117,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
||||
LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
|
||||
process_id.pid);
|
||||
|
||||
const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID());
|
||||
const auto current = GetCallerBaseTitleID(system, process_id);
|
||||
|
||||
std::vector<u32> out;
|
||||
const auto& disabled = Settings::values.disabled_addons[current];
|
||||
@@ -126,8 +131,7 @@ Result IAddOnContentManager::ListAddOnContent(Out<u32> out_count,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(DarkLordZach): Find the correct error code.
|
||||
R_UNLESS(out.size() >= offset, ResultUnknown);
|
||||
R_UNLESS(out.size() >= offset, AM::ResultApplicationRecordNotFound);
|
||||
|
||||
*out_count = static_cast<u32>(std::min<size_t>(out.size() - offset, count));
|
||||
std::rotate(out.begin(), out.begin() + offset, out.end());
|
||||
@@ -141,7 +145,7 @@ Result IAddOnContentManager::GetAddOnContentBaseId(Out<u64> out_title_id,
|
||||
ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid);
|
||||
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||
const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
|
||||
return out;
|
||||
}
|
||||
|
||||
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_)
|
||||
: ServiceFramework{system_, "IBcatService"}, backend{backend_},
|
||||
IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_)
|
||||
: ServiceFramework{system_, "IBcatService"}, backend{backend_}, program_id{program_id_},
|
||||
progress{{
|
||||
ProgressServiceBackend{system_, "Normal"},
|
||||
ProgressServiceBackend{system_, "Directory"},
|
||||
@@ -70,8 +70,7 @@ Result IBcatService::RequestSyncDeliveryCache(
|
||||
LOG_DEBUG(Service_BCAT, "called");
|
||||
|
||||
auto& progress_backend{GetProgressBackend(SyncType::Normal)};
|
||||
backend.Synchronize(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
backend.Synchronize(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
GetProgressBackend(SyncType::Normal));
|
||||
|
||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||
@@ -86,9 +85,8 @@ Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName(
|
||||
LOG_DEBUG(Service_BCAT, "called, name={}", name);
|
||||
|
||||
auto& progress_backend{GetProgressBackend(SyncType::Directory)};
|
||||
backend.SynchronizeDirectory(system.Kernel(), {system.GetApplicationProcessProgramID(),
|
||||
GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
name, progress_backend);
|
||||
backend.SynchronizeDirectory(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())},
|
||||
name, progress_backend);
|
||||
|
||||
*out_interface = std::make_shared<IDeliveryCacheProgressService>(
|
||||
system, progress_backend.GetEvent(), progress_backend.GetImpl());
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -19,7 +22,7 @@ class IDeliveryCacheProgressService;
|
||||
|
||||
class IBcatService final : public ServiceFramework<IBcatService> {
|
||||
public:
|
||||
explicit IBcatService(Core::System& system_, BcatBackend& backend_);
|
||||
explicit IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_);
|
||||
~IBcatService() override;
|
||||
|
||||
private:
|
||||
@@ -39,6 +42,7 @@ private:
|
||||
const ProgressServiceBackend& GetProgressBackend(SyncType type) const;
|
||||
|
||||
BcatBackend& backend;
|
||||
u64 program_id;
|
||||
std::array<ProgressServiceBackend, static_cast<size_t>(SyncType::Count)> progress;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// 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
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/bcat/bcat_service.h"
|
||||
#include "core/hle/service/bcat/delivery_cache_storage_service.h"
|
||||
#include "core/hle/service/bcat/service_creator.h"
|
||||
@@ -37,7 +41,8 @@ IServiceCreator::~IServiceCreator() = default;
|
||||
Result IServiceCreator::CreateBcatService(ClientProcessId process_id,
|
||||
OutInterface<IBcatService> out_interface) {
|
||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||
*out_interface = std::make_shared<IBcatService>(system, *backend);
|
||||
*out_interface =
|
||||
std::make_shared<IBcatService>(system, *backend, system.ResolveCallerProgramId(*process_id));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -45,7 +50,7 @@ Result IServiceCreator::CreateDeliveryCacheStorageService(
|
||||
ClientProcessId process_id, OutInterface<IDeliveryCacheStorageService> out_interface) {
|
||||
LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid);
|
||||
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
const auto title_id = system.ResolveCallerProgramId(*process_id);
|
||||
*out_interface =
|
||||
std::make_shared<IDeliveryCacheStorageService>(system, fsc.GetBCATDirectory(title_id));
|
||||
R_SUCCEED();
|
||||
|
||||
@@ -248,7 +248,7 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry,
|
||||
AlbumReportOption report_option,
|
||||
const ApplicationData& app_data, std::span<const u8> image_data,
|
||||
u64 aruid) {
|
||||
const u64 title_id = system.GetApplicationProcessProgramID();
|
||||
const u64 title_id = system.ResolveCallerProgramId(aruid);
|
||||
|
||||
auto static_service =
|
||||
system.ServiceManager().GetService<Service::Glue::Time::StaticService>("time:u", true);
|
||||
|
||||
@@ -95,7 +95,8 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r
|
||||
manager->FlipVerticallyOnWrite(invert_y);
|
||||
manager->SaveScreenShot(entry, attribute, report_option, image_data, {});
|
||||
},
|
||||
layout);
|
||||
layout,
|
||||
Nvnflinger::LayerStackId::Screenshot);
|
||||
}
|
||||
|
||||
} // namespace Service::Capture
|
||||
|
||||
@@ -1164,7 +1164,7 @@ Result IHidServer::InitializeSevenSixAxisSensor(ClientAppletResourceUserId aruid
|
||||
GetResourceManager()->GetConsoleSixAxis()->Activate();
|
||||
GetResourceManager()->GetSevenSixAxis()->Activate();
|
||||
|
||||
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress());
|
||||
GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress(), t_mem_1->GetOwner());
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode pollin
|
||||
|
||||
auto& device = devices[device_index.value()].device;
|
||||
device->SetPollingMode(polling_mode);
|
||||
device->SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
||||
device->SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -147,7 +150,7 @@ Result IRS::RunImageTransferProcessor(
|
||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||
image_transfer_processor.SetConfig(processor_config);
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner());
|
||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||
Common::Input::PollingMode::IR);
|
||||
|
||||
@@ -295,7 +298,8 @@ Result IRS::RunImageTransferExProcessor(
|
||||
MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device);
|
||||
auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle);
|
||||
image_transfer_processor.SetConfig(processor_config);
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress());
|
||||
image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(),
|
||||
t_mem->GetOwner());
|
||||
npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex,
|
||||
Common::Input::PollingMode::IR);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
, process{kernel, process_}
|
||||
, user_rx{std::move(user_rx_)}
|
||||
, user_ro{std::move(user_ro_)}
|
||||
, context{system_.ApplicationMemory()}
|
||||
, context{process_->GetMemory()}
|
||||
{
|
||||
|
||||
// clang-format off
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/vfs/vfs.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_transfer_memory.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/ns/language.h"
|
||||
@@ -336,8 +337,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
|
||||
|
||||
constexpr s32 data_offset = 0;
|
||||
|
||||
if (t_mem != nullptr && app_count > 0) {
|
||||
auto& memory = system.ApplicationMemory();
|
||||
if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) {
|
||||
auto& memory = t_mem->GetOwner()->GetMemory();
|
||||
const auto t_mem_address = t_mem->GetSourceAddress();
|
||||
|
||||
for (size_t i = 0; i < app_count; ++i) {
|
||||
|
||||
@@ -76,6 +76,7 @@ void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers
|
||||
.transform_flags = layer.transform,
|
||||
.crop_rect = layer.crop_rect,
|
||||
.blending = ConvertBlending(layer.blending),
|
||||
.layer_stack_mask = layer.layer_stack_mask,
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) {
|
||||
|
||||
@@ -69,7 +69,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
||||
case 0x3:
|
||||
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output);
|
||||
case 0x8:
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false);
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, false);
|
||||
case 0x9:
|
||||
return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output);
|
||||
case 0xb:
|
||||
@@ -83,7 +83,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span<const u8> inpu
|
||||
case 0x1a:
|
||||
return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output, fd);
|
||||
case 0x1b:
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true);
|
||||
return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, true);
|
||||
case 0x1d:
|
||||
return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output);
|
||||
default:
|
||||
@@ -387,8 +387,19 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
Core::Memory::Memory& nvhost_gpu::GetSessionMemory(DeviceFD fd) {
|
||||
if (const auto it = sessions.find(fd); it != sessions.end())
|
||||
if (auto* const session = core.GetSession(it->second);
|
||||
session != nullptr && session->process != nullptr)
|
||||
return session->process->GetMemory();
|
||||
|
||||
LOG_ERROR(Service_NVDRV, "No session for fd={}, falling back to application memory", fd);
|
||||
return system.ApplicationMemory();
|
||||
}
|
||||
|
||||
NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
||||
std::span<Tegra::CommandListHeader> commands, bool kickoff) {
|
||||
std::span<Tegra::CommandListHeader> commands, DeviceFD fd,
|
||||
bool kickoff) {
|
||||
if (params.num_entries > commands.size()) {
|
||||
UNIMPLEMENTED();
|
||||
return NvResult::InvalidSize;
|
||||
@@ -396,7 +407,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
||||
|
||||
Tegra::CommandList entries(params.num_entries);
|
||||
if (kickoff) {
|
||||
system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(),
|
||||
this->GetSessionMemory(fd).ReadBlock(params.address, entries.command_lists.data(),
|
||||
params.num_entries * sizeof(Tegra::CommandListHeader));
|
||||
} else {
|
||||
std::memcpy(entries.command_lists.data(), commands.data(),
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
#include "video_core/dma_pusher.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
}
|
||||
|
||||
namespace Tegra {
|
||||
namespace Control {
|
||||
struct ChannelState;
|
||||
@@ -196,8 +200,11 @@ private:
|
||||
|
||||
NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries);
|
||||
|
||||
Core::Memory::Memory& GetSessionMemory(DeviceFD fd);
|
||||
|
||||
NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params,
|
||||
std::span<Tegra::CommandListHeader> commands, bool kickoff = false);
|
||||
std::span<Tegra::CommandListHeader> commands, DeviceFD fd,
|
||||
bool kickoff = false);
|
||||
NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params,
|
||||
std::span<const Tegra::CommandListHeader> commands);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -15,7 +15,8 @@ struct Layer {
|
||||
explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
|
||||
s32 consumer_id_)
|
||||
: buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
|
||||
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false) {}
|
||||
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false),
|
||||
layer_stack_mask(DefaultLayerStackMask) {}
|
||||
~Layer() {
|
||||
buffer_item_consumer->Abandon();
|
||||
}
|
||||
@@ -26,6 +27,7 @@ struct Layer {
|
||||
bool visible;
|
||||
s32 z_index;
|
||||
bool is_overlay;
|
||||
u32 layer_stack_mask;
|
||||
};
|
||||
|
||||
struct LayerStack {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -125,6 +125,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
|
||||
.transform = static_cast<android::BufferTransformFlags>(item.transform),
|
||||
.crop_rect = item.crop,
|
||||
.acquire_fence = item.fence,
|
||||
.layer_stack_mask = layer->layer_stack_mask,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -23,6 +26,25 @@ enum class LayerBlending : u32 {
|
||||
Coverage = 0x405,
|
||||
};
|
||||
|
||||
enum class LayerStackId : u32 {
|
||||
Default = 0,
|
||||
Lcd = 1,
|
||||
Screenshot = 2,
|
||||
Recording = 3,
|
||||
LastFrame = 4,
|
||||
Arbitrary = 5,
|
||||
ApplicationForDebug = 6,
|
||||
Null = 10,
|
||||
};
|
||||
|
||||
constexpr u32 LayerStackBit(LayerStackId id) {
|
||||
return 1U << static_cast<u32>(id);
|
||||
}
|
||||
|
||||
constexpr u32 DefaultLayerStackMask =
|
||||
LayerStackBit(LayerStackId::Default) | LayerStackBit(LayerStackId::Screenshot) |
|
||||
LayerStackBit(LayerStackId::Recording) | LayerStackBit(LayerStackId::LastFrame);
|
||||
|
||||
struct HwcLayer {
|
||||
u32 buffer_handle;
|
||||
u32 offset;
|
||||
@@ -35,6 +57,7 @@ struct HwcLayer {
|
||||
android::BufferTransformFlags transform;
|
||||
Common::Rectangle<int> crop_rect;
|
||||
android::Fence acquire_fence;
|
||||
u32 layer_stack_mask;
|
||||
};
|
||||
|
||||
} // namespace Service::Nvnflinger
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -104,6 +104,14 @@ void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blen
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceFlinger::SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask) {
|
||||
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
|
||||
layer->layer_stack_mask = layer_stack_mask;
|
||||
LOG_DEBUG(Service_VI, "Layer {} stack mask set to {:#x}", consumer_binder_id,
|
||||
layer_stack_mask);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceFlinger::SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay) {
|
||||
if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) {
|
||||
layer->is_overlay = is_overlay;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -48,6 +48,7 @@ public:
|
||||
void SetLayerVisibility(s32 consumer_binder_id, bool visible);
|
||||
void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
|
||||
void SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay);
|
||||
void SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask);
|
||||
|
||||
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -13,8 +13,10 @@
|
||||
|
||||
namespace Service::PCTL {
|
||||
|
||||
IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_)
|
||||
IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_,
|
||||
u64 program_id_)
|
||||
: ServiceFramework{system_, "IParentalControlService"}, capability{capability_},
|
||||
program_id{program_id_},
|
||||
service_context{system_, "IParentalControlService"}, synchronization_event{service_context},
|
||||
unlinked_event{service_context}, request_suspension_event{service_context} {
|
||||
// clang-format off
|
||||
@@ -202,7 +204,6 @@ Result IParentalControlService::Initialize() {
|
||||
|
||||
// TODO(ogniK): Recovery flag initialization for pctl:r
|
||||
|
||||
const auto program_id = system.GetApplicationProcessProgramID();
|
||||
if (program_id != 0) {
|
||||
const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
|
||||
system.GetContentProvider()};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator
|
||||
@@ -16,7 +16,8 @@ namespace Service::PCTL {
|
||||
|
||||
class IParentalControlService final : public ServiceFramework<IParentalControlService> {
|
||||
public:
|
||||
explicit IParentalControlService(Core::System& system_, Capability capability_);
|
||||
explicit IParentalControlService(Core::System& system_, Capability capability_,
|
||||
u64 program_id_);
|
||||
~IParentalControlService() override;
|
||||
|
||||
private:
|
||||
@@ -84,6 +85,7 @@ private:
|
||||
RestrictionSettings restriction_settings{};
|
||||
std::array<char, 8> pin_code{};
|
||||
Capability capability{};
|
||||
u64 program_id{};
|
||||
// TODO: this is RAW as fuck
|
||||
PlayTimerSettings raw_play_timer_settings{};
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/service/cmif_serialization.h"
|
||||
#include "core/hle/service/pctl/parental_control_service.h"
|
||||
#include "core/hle/service/pctl/parental_control_service_factory.h"
|
||||
@@ -23,17 +27,17 @@ IParentalControlServiceFactory::~IParentalControlServiceFactory() = default;
|
||||
|
||||
Result IParentalControlServiceFactory::CreateService(
|
||||
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_PCTL, "called");
|
||||
// TODO(ogniK): Get application id from process
|
||||
*out_service = std::make_shared<IParentalControlService>(system, capability);
|
||||
LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid);
|
||||
*out_service = std::make_shared<IParentalControlService>(
|
||||
system, capability, system.ResolveCallerProgramId(*process_id));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IParentalControlServiceFactory::CreateServiceWithoutInitialize(
|
||||
Out<SharedPointer<IParentalControlService>> out_service, ClientProcessId process_id) {
|
||||
LOG_DEBUG(Service_PCTL, "called");
|
||||
// TODO(ogniK): Get application id from process
|
||||
*out_service = std::make_shared<IParentalControlService>(system, capability);
|
||||
LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid);
|
||||
*out_service = std::make_shared<IParentalControlService>(
|
||||
system, capability, system.ResolveCallerProgramId(*process_id));
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -132,8 +132,7 @@ private:
|
||||
|
||||
LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid);
|
||||
|
||||
auto list = kernel.GetProcessList();
|
||||
auto process = SearchProcessList(system.Kernel(), list, [pid](auto& p) { return p->GetProcessId() == pid; });
|
||||
auto process = kernel.GetProcessByProcessId(pid);
|
||||
|
||||
if (process.IsNull()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -186,9 +185,7 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id);
|
||||
|
||||
auto list = kernel.GetProcessList();
|
||||
auto process = SearchProcessList(system.Kernel(),
|
||||
list, [process_id](auto& p) { return p->GetProcessId() == process_id; });
|
||||
auto process = kernel.GetProcessByProcessId(process_id);
|
||||
|
||||
if (process.IsNull()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -76,7 +76,7 @@ private:
|
||||
Type, process_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
|
||||
reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2},
|
||||
process_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@@ -98,7 +98,7 @@ private:
|
||||
Type, user_id[1], user_id[0], process_id, data1.size(), data2.size());
|
||||
|
||||
const auto& reporter{system.GetReporter()};
|
||||
reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2},
|
||||
reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2},
|
||||
process_id, user_id);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -166,6 +166,16 @@ Result Container::GetLayerZIndex(u64 layer_id, s32* out_z_index) {
|
||||
R_RETURN(VI::ResultNotFound);
|
||||
}
|
||||
|
||||
Result Container::SetLayerStackMask(u64 layer_id, u32 layer_stack_mask) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
auto* const layer = m_layers.GetLayerById(layer_id);
|
||||
R_UNLESS(layer != nullptr, VI::ResultNotFound);
|
||||
|
||||
m_surface_flinger->SetLayerStackMask(layer->GetConsumerBinderId(), layer_stack_mask);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result Container::SetLayerIsOverlay(u64 layer_id, bool is_overlay) {
|
||||
std::scoped_lock lk{m_lock};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
@@ -68,6 +68,7 @@ public:
|
||||
Result SetLayerZIndex(u64 layer_id, s32 z_index);
|
||||
Result GetLayerZIndex(u64 layer_id, s32* out_z_index);
|
||||
Result SetLayerIsOverlay(u64 layer_id, bool is_overlay);
|
||||
Result SetLayerStackMask(u64 layer_id, u32 layer_stack_mask);
|
||||
|
||||
void LinkVsyncEvent(u64 display_id, Event* event);
|
||||
void UnlinkVsyncEvent(u64 display_id, Event* event);
|
||||
|
||||
@@ -4,9 +4,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <random>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/scratch_buffer.h"
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_page_group.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvmap.h"
|
||||
@@ -47,7 +53,7 @@ Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_
|
||||
u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
|
||||
|
||||
for (; start < end; start++) {
|
||||
*start = 0xFF0000FF;
|
||||
*start = 0x00000000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +174,14 @@ constexpr u32 SharedBufferBlockLinearWidth = 1280;
|
||||
constexpr u32 SharedBufferBlockLinearHeight = 768;
|
||||
constexpr u32 SharedBufferBlockLinearStride =
|
||||
SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp;
|
||||
constexpr u32 SharedBufferNumSlots = 7;
|
||||
|
||||
constexpr u32 SharedBufferNumCaptureSlots = 3;
|
||||
constexpr u32 SharedBufferSlotsPerSession = 2;
|
||||
constexpr u32 SharedBufferMaxSessions = 2;
|
||||
|
||||
constexpr u32 SharedBufferNumSlots =
|
||||
SharedBufferNumCaptureSlots + SharedBufferSlotsPerSession * SharedBufferMaxSessions;
|
||||
static_assert(SharedBufferNumSlots <= 16, "Shared buffer pool exceeds the maximum texture count");
|
||||
|
||||
constexpr u32 SharedBufferWidth = 1280;
|
||||
constexpr u32 SharedBufferHeight = 720;
|
||||
@@ -192,7 +205,52 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
|
||||
return layout;
|
||||
}();
|
||||
|
||||
void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
|
||||
constexpr u32 GetCaptureSlot(CaptureKind kind) {
|
||||
return static_cast<u32>(kind);
|
||||
}
|
||||
static_assert(static_cast<u32>(CaptureKind::CallerApplet) + 1 == SharedBufferNumCaptureSlots,
|
||||
"Capture slot count does not match CaptureKind");
|
||||
|
||||
constexpr u32 GetPresentationSlot(u32 slot_base, u32 index) {
|
||||
return SharedBufferNumCaptureSlots + slot_base + index;
|
||||
}
|
||||
|
||||
constexpr u32 ColorOpaqueBlackRgba32 = 0xFF000000;
|
||||
|
||||
template <typename F>
|
||||
void ForEachPoolChunk(Core::System& system, Kernel::KPageGroup& page_group, u64 offset, u64 size,
|
||||
F&& writer) {
|
||||
Common::ScratchBuffer<u32> scratch;
|
||||
const u64 range_end = offset + size;
|
||||
u64 pool_pos = 0;
|
||||
|
||||
for (auto& block : page_group) {
|
||||
const u64 block_begin = pool_pos;
|
||||
const u64 block_end = block_begin + block.GetSize();
|
||||
pool_pos = block_end;
|
||||
|
||||
if (block_end <= offset) {
|
||||
continue;
|
||||
}
|
||||
if (block_begin >= range_end) {
|
||||
break;
|
||||
}
|
||||
|
||||
const u64 chunk_begin = (std::max)(block_begin, offset);
|
||||
const u64 chunk_end = (std::min)(block_end, range_end);
|
||||
const u64 chunk_size = chunk_end - chunk_begin;
|
||||
|
||||
u8* const dst =
|
||||
system.DeviceMemory().GetPointer<u8>(block.GetAddress()) + (chunk_begin - block_begin);
|
||||
|
||||
writer(dst, chunk_begin - offset, chunk_size);
|
||||
|
||||
system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(
|
||||
dst, scratch, [&](DAddr addr) { system.GPU().InvalidateRegion(addr, chunk_size); });
|
||||
}
|
||||
}
|
||||
|
||||
void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 producer_slot, u32 pool_slot, u32 handle) {
|
||||
auto buffer = std::make_shared<android::NvGraphicBuffer>();
|
||||
buffer->width = SharedBufferWidth;
|
||||
buffer->height = SharedBufferHeight;
|
||||
@@ -200,8 +258,8 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 han
|
||||
buffer->format = SharedBufferBlockLinearFormat;
|
||||
buffer->external_format = SharedBufferBlockLinearFormat;
|
||||
buffer->buffer_id = handle;
|
||||
buffer->offset = slot * SharedBufferSlotSize;
|
||||
ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
|
||||
buffer->offset = pool_slot * SharedBufferSlotSize;
|
||||
ASSERT(producer.SetPreallocatedBuffer(producer_slot, buffer) == android::Status::NoError);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -215,59 +273,91 @@ SharedBufferManager::~SharedBufferManager() = default;
|
||||
Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id,
|
||||
u64* out_layer_handle, u64 display_id,
|
||||
bool enable_blending) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
{
|
||||
std::scoped_lock lk{m_guard};
|
||||
|
||||
// Ensure we haven't already created.
|
||||
const u64 aruid = owner_process->GetProcessId();
|
||||
R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
|
||||
// Ensure we haven't already created.
|
||||
const u64 aruid = owner_process->GetProcessId();
|
||||
R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied);
|
||||
|
||||
// Allocate memory for the shared buffer if needed.
|
||||
if (!m_buffer_page_group) {
|
||||
R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
|
||||
SharedBufferSize));
|
||||
// Allocate memory for the shared buffer if needed.
|
||||
if (!m_buffer_page_group) {
|
||||
R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system,
|
||||
SharedBufferSize));
|
||||
|
||||
// Record buffer id.
|
||||
m_buffer_id = m_next_buffer_id++;
|
||||
// Record buffer id.
|
||||
m_buffer_id = m_next_buffer_id++;
|
||||
|
||||
// Record display id.
|
||||
m_display_id = display_id;
|
||||
// Record display id.
|
||||
m_display_id = display_id;
|
||||
|
||||
for (u32 slot = 0; slot < SharedBufferNumCaptureSlots; slot++) {
|
||||
ForEachPoolChunk(m_system, *m_buffer_page_group, u64{slot} * SharedBufferSlotSize,
|
||||
SharedBufferSlotSize, [](u8* dst, u64, u64 length) {
|
||||
std::fill_n(reinterpret_cast<u32*>(dst), length / sizeof(u32),
|
||||
ColorOpaqueBlackRgba32);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Claim a presentation slot range.
|
||||
u32 slot_base = 0;
|
||||
|
||||
std::array<bool, SharedBufferMaxSessions> in_use{};
|
||||
for (const auto& [existing_aruid, existing] : m_sessions) {
|
||||
const u32 index = existing.presentation_slot_base / SharedBufferSlotsPerSession;
|
||||
|
||||
if (index < in_use.size())
|
||||
in_use[index] = true;
|
||||
}
|
||||
|
||||
u32 index = 0;
|
||||
while (index < in_use.size() && in_use[index])
|
||||
index++;
|
||||
|
||||
if (index >= in_use.size()) {
|
||||
LOG_ERROR(Service_VI, "Out of shared buffer presentation slots ({} sessions)", SharedBufferMaxSessions);
|
||||
R_THROW(VI::ResultOperationFailed);
|
||||
}
|
||||
|
||||
slot_base = index * SharedBufferSlotsPerSession;
|
||||
|
||||
// Map into process.
|
||||
Common::ProcessAddress map_address{};
|
||||
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
|
||||
owner_process, m_system));
|
||||
|
||||
// Create new session.
|
||||
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
|
||||
auto& session = it->second;
|
||||
session.presentation_slot_base = slot_base;
|
||||
|
||||
auto& container = m_nvdrv->GetContainer();
|
||||
session.session_id = container.OpenSession(owner_process);
|
||||
session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
|
||||
|
||||
// Create an nvmap handle for the buffer and assign the memory to it.
|
||||
R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
|
||||
session.nvmap_fd, map_address, SharedBufferSize));
|
||||
|
||||
// Create and open a layer for the display.
|
||||
s32 producer_binder_id;
|
||||
R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
|
||||
std::addressof(session.layer_id), display_id));
|
||||
|
||||
// Configure blending and z-index
|
||||
R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));
|
||||
|
||||
// Get the producer and set preallocated buffers.
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id));
|
||||
for (u32 i = 0; i < SharedBufferSlotsPerSession; i++)
|
||||
MakeGraphicBuffer(*producer, i, GetPresentationSlot(session.presentation_slot_base, i), session.buffer_nvmap_handle);
|
||||
|
||||
// Assign outputs.
|
||||
*out_buffer_id = m_buffer_id;
|
||||
*out_layer_handle = session.layer_id;
|
||||
}
|
||||
|
||||
// Map into process.
|
||||
Common::ProcessAddress map_address{};
|
||||
R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group,
|
||||
owner_process, m_system));
|
||||
|
||||
// Create new session.
|
||||
auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{});
|
||||
auto& session = it->second;
|
||||
|
||||
auto& container = m_nvdrv->GetContainer();
|
||||
session.session_id = container.OpenSession(owner_process);
|
||||
session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id);
|
||||
|
||||
// Create an nvmap handle for the buffer and assign the memory to it.
|
||||
R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv,
|
||||
session.nvmap_fd, map_address, SharedBufferSize));
|
||||
|
||||
// Create and open a layer for the display.
|
||||
s32 producer_binder_id;
|
||||
R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
|
||||
std::addressof(session.layer_id), display_id));
|
||||
|
||||
// Configure blending and z-index
|
||||
R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));
|
||||
|
||||
// Get the producer and set preallocated buffers.
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id));
|
||||
MakeGraphicBuffer(*producer, 0, session.buffer_nvmap_handle);
|
||||
MakeGraphicBuffer(*producer, 1, session.buffer_nvmap_handle);
|
||||
|
||||
// Assign outputs.
|
||||
*out_buffer_id = m_buffer_id;
|
||||
*out_layer_handle = session.layer_id;
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -336,14 +426,51 @@ Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
|
||||
SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
// Assign remaining outputs.
|
||||
*out_target_slot = slot;
|
||||
out_slot_indexes = {0, 1, -1, -1};
|
||||
out_slot_indexes.fill(-1);
|
||||
|
||||
{
|
||||
std::scoped_lock lk{m_guard};
|
||||
const auto* const session = this->FindSessionByLayerIdLocked(layer_id);
|
||||
if (session == nullptr) {
|
||||
producer->CancelBuffer(slot, *out_fence);
|
||||
// LOG_DEBUG(Service_VI, "No Session found");
|
||||
R_THROW(VI::ResultNotFound);
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < SharedBufferSlotsPerSession; i++)
|
||||
out_slot_indexes[i] =
|
||||
static_cast<s32>(GetPresentationSlot(session->presentation_slot_base, i));
|
||||
|
||||
*out_target_slot = static_cast<s64>(
|
||||
GetPresentationSlot(session->presentation_slot_base, static_cast<u32>(slot)));
|
||||
}
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SharedBufferManager::GetProducerSlotLocked(s32* out_producer_slot, u64 layer_id,
|
||||
s64 pool_slot) const {
|
||||
const auto* const session = this->FindSessionByLayerIdLocked(layer_id);
|
||||
R_UNLESS(session != nullptr, VI::ResultNotFound);
|
||||
|
||||
const s64 base = GetPresentationSlot(session->presentation_slot_base, 0);
|
||||
const s64 producer_slot = pool_slot - base;
|
||||
R_UNLESS(producer_slot >= 0 && producer_slot < SharedBufferSlotsPerSession,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
*out_producer_slot = static_cast<s32>(producer_slot);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
const SharedBufferSession* SharedBufferManager::FindSessionByLayerIdLocked(u64 layer_id) const {
|
||||
for (const auto& [aruid, session] : m_sessions)
|
||||
if (session.layer_id == layer_id)
|
||||
return std::addressof(session);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
Common::Rectangle<s32> crop_region,
|
||||
u32 transform, s32 swap_interval, u64 layer_id,
|
||||
@@ -352,14 +479,20 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
|
||||
|
||||
s32 producer_slot;
|
||||
{
|
||||
std::scoped_lock lk{m_guard};
|
||||
R_TRY(this->GetProducerSlotLocked(std::addressof(producer_slot), layer_id, slot));
|
||||
}
|
||||
|
||||
// Request to queue the buffer.
|
||||
std::shared_ptr<android::GraphicBuffer> buffer;
|
||||
R_UNLESS(producer->RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
|
||||
R_UNLESS(producer->RequestBuffer(producer_slot, std::addressof(buffer)) ==
|
||||
android::Status::NoError,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
ON_RESULT_FAILURE {
|
||||
producer->CancelBuffer(static_cast<s32>(slot), fence);
|
||||
producer->CancelBuffer(producer_slot, fence);
|
||||
};
|
||||
|
||||
// Queue the buffer to the producer.
|
||||
@@ -369,12 +502,10 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
|
||||
input.fence = fence;
|
||||
input.transform = static_cast<android::NativeWindowTransform>(transform);
|
||||
input.swap_interval = swap_interval;
|
||||
R_UNLESS(producer->QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
|
||||
R_UNLESS(producer->QueueBuffer(producer_slot, input, std::addressof(output)) ==
|
||||
android::Status::NoError,
|
||||
VI::ResultOperationFailed);
|
||||
|
||||
(void)m_container.SetLayerZIndex(layer_id, 100000);
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
}
|
||||
@@ -384,8 +515,14 @@ Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) {
|
||||
std::shared_ptr<android::BufferQueueProducer> producer;
|
||||
R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id));
|
||||
|
||||
s32 producer_slot;
|
||||
{
|
||||
std::scoped_lock lk{m_guard};
|
||||
R_TRY(this->GetProducerSlotLocked(std::addressof(producer_slot), layer_id, slot));
|
||||
}
|
||||
|
||||
// Cancel.
|
||||
producer->CancelBuffer(static_cast<s32>(slot), android::Fence::NoFence());
|
||||
producer->CancelBuffer(producer_slot, android::Fence::NoFence());
|
||||
|
||||
// We succeeded.
|
||||
R_SUCCEED();
|
||||
@@ -404,31 +541,47 @@ Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadabl
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) {
|
||||
std::vector<u8> capture_buffer(m_system.GPU().GetAppletCaptureBuffer());
|
||||
Common::ScratchBuffer<u32> scratch;
|
||||
Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index, CaptureKind kind) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
R_UNLESS(m_buffer_page_group != nullptr, VI::ResultNotFound);
|
||||
|
||||
// TODO: this could be optimized
|
||||
s64 e = -1280 * 768 * 4;
|
||||
for (auto& block : *m_buffer_page_group) {
|
||||
u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
|
||||
u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
|
||||
const std::vector<u8> capture = m_system.GPU().GetAppletCaptureBuffer();
|
||||
const u32 slot = GetCaptureSlot(kind);
|
||||
|
||||
for (; start < end; start++) {
|
||||
*start = 0;
|
||||
if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
|
||||
*start = capture_buffer[e];
|
||||
}
|
||||
e++;
|
||||
}
|
||||
|
||||
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
|
||||
m_system.GPU().InvalidateRegion(addr, end - start);
|
||||
});
|
||||
if (capture.size() < SharedBufferSlotSize) {
|
||||
//LOG_WARNING(Service_VI, "Capture buffer is {} bytes, expected at least {}; not writing",
|
||||
// capture.size(), SharedBufferSlotSize);
|
||||
*out_was_written = false;
|
||||
*out_layer_index = static_cast<s32>(slot);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
ForEachPoolChunk(m_system, *m_buffer_page_group, u64{slot} * SharedBufferSlotSize,
|
||||
SharedBufferSlotSize,
|
||||
[&](u8* dst, u64 src_offset, u64 length) {
|
||||
std::memcpy(dst, capture.data() + src_offset, length);
|
||||
});
|
||||
|
||||
*out_was_written = true;
|
||||
*out_layer_index = 1;
|
||||
*out_layer_index = static_cast<s32>(slot);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result SharedBufferManager::ClearAppletCaptureBuffer(s32 layer_index, u32 color) {
|
||||
std::scoped_lock lk{m_guard};
|
||||
R_UNLESS(m_buffer_page_group != nullptr, VI::ResultNotFound);
|
||||
|
||||
if (layer_index < 0 || layer_index >= static_cast<s32>(SharedBufferNumCaptureSlots)) {
|
||||
LOG_WARNING(Service_VI, "Couldnt clear non-capture slot {}", layer_index);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
ForEachPoolChunk(m_system, *m_buffer_page_group, u64{static_cast<u32>(layer_index)} * SharedBufferSlotSize,
|
||||
SharedBufferSlotSize, [&](u8* dst, u64 src_offset, u64 length) {
|
||||
ASSERT(src_offset % sizeof(u32) == 0 && length % sizeof(u32) == 0);
|
||||
std::fill_n(reinterpret_cast<u32*>(dst), length / sizeof(u32), color);
|
||||
});
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
@@ -48,6 +48,12 @@ static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout h
|
||||
|
||||
struct SharedBufferSession;
|
||||
|
||||
enum class CaptureKind : u32 {
|
||||
LastApplication,
|
||||
LastForeground,
|
||||
CallerApplet,
|
||||
};
|
||||
|
||||
class SharedBufferManager final {
|
||||
public:
|
||||
explicit SharedBufferManager(Core::System& system, Container& container,
|
||||
@@ -68,9 +74,15 @@ public:
|
||||
Result CancelSharedFrameBuffer(u64 layer_id, s64 slot);
|
||||
Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
|
||||
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index);
|
||||
Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index, CaptureKind kind);
|
||||
Result ClearAppletCaptureBuffer(s32 layer_index, u32 color);
|
||||
|
||||
private:
|
||||
const SharedBufferSession* FindSessionByLayerIdLocked(u64 layer_id) const;
|
||||
|
||||
/// Converts a pool slot index, which is what the guest works in, back to the buffer queues slot index
|
||||
Result GetProducerSlotLocked(s32* out_producer_slot, u64 layer_id, s64 pool_slot) const;
|
||||
|
||||
u64 m_next_buffer_id = 1;
|
||||
u64 m_display_id = 0;
|
||||
u64 m_buffer_id = 0;
|
||||
@@ -89,6 +101,7 @@ struct SharedBufferSession {
|
||||
Nvidia::NvCore::SessionId session_id = {};
|
||||
u64 layer_id = {};
|
||||
u32 buffer_nvmap_handle = 0;
|
||||
u32 presentation_slot_base = 0;
|
||||
};
|
||||
|
||||
} // namespace Service::VI
|
||||
|
||||
@@ -209,7 +209,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
|
||||
|
||||
// Apply cheats if they exist and the program has a valid title ID
|
||||
if (pm) {
|
||||
system.SetApplicationProcessBuildID(nso_header.build_id);
|
||||
// TODO(Maufeat): Check if there is a better way to check
|
||||
if (name == "main")
|
||||
system.SetApplicationProcessBuildID(nso_header.build_id);
|
||||
|
||||
const auto cheats = pm->CreateCheatList(nso_header.build_id);
|
||||
if (!cheats.empty()) {
|
||||
system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
|
||||
|
||||
@@ -51,24 +51,41 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta
|
||||
|
||||
StandardVmCallbacks::~StandardVmCallbacks() = default;
|
||||
|
||||
Kernel::KProcess* StandardVmCallbacks::GetProcess() const {
|
||||
if (cached_process != nullptr && cached_process_id == metadata.process_id) {
|
||||
return cached_process;
|
||||
}
|
||||
|
||||
auto process = system.Kernel().GetProcessByProcessId(metadata.process_id);
|
||||
cached_process = process.IsNull() ? nullptr : process.GetPointerUnsafe();
|
||||
cached_process_id = metadata.process_id;
|
||||
return cached_process;
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) {
|
||||
auto* const process = this->GetProcess();
|
||||
|
||||
// Return zero on invalid address
|
||||
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
|
||||
if (process == nullptr || !IsAddressInRange(address) ||
|
||||
!process->GetMemory().IsValidVirtualAddress(address)) {
|
||||
std::memset(data, 0, size);
|
||||
return;
|
||||
}
|
||||
|
||||
system.ApplicationMemory().ReadBlock(address, data, size);
|
||||
process->GetMemory().ReadBlock(address, data, size);
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) {
|
||||
auto* const process = this->GetProcess();
|
||||
|
||||
// Skip invalid memory write address
|
||||
if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) {
|
||||
if (process == nullptr || !IsAddressInRange(address) ||
|
||||
!process->GetMemory().IsValidVirtualAddress(address)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (system.ApplicationMemory().WriteBlock(address, data, size)) {
|
||||
Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size);
|
||||
if (process->GetMemory().WriteBlock(address, data, size)) {
|
||||
Core::InvalidateInstructionCacheRange(process, address, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,14 +108,16 @@ u64 StandardVmCallbacks::HidKeysDown() {
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::PauseProcess() {
|
||||
if (!system.ApplicationProcess()->IsSuspended()) {
|
||||
system.ApplicationProcess()->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Paused);
|
||||
auto* const process = this->GetProcess();
|
||||
if (process != nullptr && !process->IsSuspended()) {
|
||||
process->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Paused);
|
||||
}
|
||||
}
|
||||
|
||||
void StandardVmCallbacks::ResumeProcess() {
|
||||
if (system.ApplicationProcess()->IsSuspended()) {
|
||||
system.ApplicationProcess()->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Runnable);
|
||||
auto* const process = this->GetProcess();
|
||||
if (process != nullptr && process->IsSuspended()) {
|
||||
process->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Runnable);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -15,6 +18,10 @@ namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Core::Timing {
|
||||
class CoreTiming;
|
||||
struct EventType;
|
||||
@@ -38,8 +45,12 @@ public:
|
||||
private:
|
||||
bool IsAddressInRange(VAddr address) const;
|
||||
|
||||
Kernel::KProcess* GetProcess() const;
|
||||
|
||||
const CheatProcessMetadata& metadata;
|
||||
Core::System& system;
|
||||
mutable Kernel::KProcess* cached_process{};
|
||||
mutable u64 cached_process_id{};
|
||||
};
|
||||
|
||||
// Intermediary class that parses a text file or other disk format for storing cheats into a
|
||||
|
||||
@@ -239,7 +239,7 @@ void Reporter::SaveUnimplementedFunctionReport(Service::HLERequestContext& ctx,
|
||||
const auto title_id = system.GetApplicationProcessProgramID();
|
||||
auto out = GetFullDataAuto(timestamp, title_id, system);
|
||||
|
||||
auto function_out = GetHLERequestContextData(ctx, system.ApplicationMemory());
|
||||
auto function_out = GetHLERequestContextData(ctx, ctx.GetMemory());
|
||||
function_out["command_id"] = command_id;
|
||||
function_out["function_name"] = name;
|
||||
function_out["service_name"] = service_name;
|
||||
|
||||
@@ -100,6 +100,7 @@ void EmulatedConsole::ReloadInput() {
|
||||
motion.gyro = emulated_motion.GetGyroscope();
|
||||
motion.rotation = emulated_motion.GetRotations();
|
||||
motion.orientation = emulated_motion.GetOrientation();
|
||||
motion.quaternion = emulated_motion.GetQuaternion();
|
||||
motion.is_at_rest = !emulated_motion.IsMoving(motion_sensitivity);
|
||||
|
||||
// Unique index for identifying touch device source
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -62,8 +65,9 @@ void HidbusBase::DisablePollingMode() {
|
||||
polling_mode_enabled = false;
|
||||
}
|
||||
|
||||
void HidbusBase::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
|
||||
void HidbusBase::SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner) {
|
||||
transfer_memory = t_mem;
|
||||
transfer_memory_owner = owner;
|
||||
}
|
||||
|
||||
Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// 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
|
||||
|
||||
@@ -14,6 +17,7 @@ class System;
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KProcess;
|
||||
class KReadableEvent;
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -138,7 +142,7 @@ public:
|
||||
void DisablePollingMode();
|
||||
|
||||
// Called on EnableJoyPollingReceiveMode
|
||||
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
|
||||
void SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner);
|
||||
|
||||
Kernel::KReadableEvent& GetSendCommandAsycEvent() const;
|
||||
|
||||
@@ -175,6 +179,7 @@ protected:
|
||||
ButtonOnlyPollingDataAccessor button_only_data{};
|
||||
|
||||
Common::ProcessAddress transfer_memory{};
|
||||
Kernel::KProcess* transfer_memory_owner{};
|
||||
|
||||
Core::System& system;
|
||||
Kernel::KEvent* send_command_async_event;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_readable_event.h"
|
||||
#include "core/memory.h"
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
@@ -67,8 +68,10 @@ void RingController::OnUpdate() {
|
||||
curr_entry.polling_data.out_size = sizeof(ringcon_value);
|
||||
std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value));
|
||||
|
||||
system.ApplicationMemory().WriteBlock(transfer_memory, &enable_sixaxis_data,
|
||||
sizeof(enable_sixaxis_data));
|
||||
if (transfer_memory_owner != nullptr) {
|
||||
transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &enable_sixaxis_data,
|
||||
sizeof(enable_sixaxis_data));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/memory.h"
|
||||
#include "hid_core/frontend/emulated_controller.h"
|
||||
#include "hid_core/hid_core.h"
|
||||
@@ -48,10 +49,12 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
|
||||
if (type != Core::HID::ControllerTriggerType::IrSensor) {
|
||||
return;
|
||||
}
|
||||
if (transfer_memory == 0) {
|
||||
if (transfer_memory == 0 || transfer_memory_owner == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& memory = transfer_memory_owner->GetMemory();
|
||||
|
||||
const auto& camera_data = npad_device->GetCamera();
|
||||
|
||||
// This indicates how much ambient light is present
|
||||
@@ -61,16 +64,14 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
|
||||
if (camera_data.format != current_config.origin_format) {
|
||||
LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format,
|
||||
current_config.origin_format);
|
||||
system.ApplicationMemory().ZeroBlock(transfer_memory,
|
||||
GetDataSize(current_config.trimming_format));
|
||||
memory.ZeroBlock(transfer_memory, GetDataSize(current_config.trimming_format));
|
||||
return;
|
||||
}
|
||||
|
||||
if (current_config.origin_format > current_config.trimming_format) {
|
||||
LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}",
|
||||
current_config.origin_format, current_config.trimming_format);
|
||||
system.ApplicationMemory().ZeroBlock(transfer_memory,
|
||||
GetDataSize(current_config.trimming_format));
|
||||
memory.ZeroBlock(transfer_memory, GetDataSize(current_config.trimming_format));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -87,8 +88,7 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
|
||||
"Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})",
|
||||
current_config.trimming_start_x, current_config.trimming_start_y,
|
||||
trimming_width, trimming_height, origin_width, origin_height);
|
||||
system.ApplicationMemory().ZeroBlock(transfer_memory,
|
||||
GetDataSize(current_config.trimming_format));
|
||||
memory.ZeroBlock(transfer_memory, GetDataSize(current_config.trimming_format));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,8 +102,8 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType
|
||||
}
|
||||
}
|
||||
|
||||
system.ApplicationMemory().WriteBlock(transfer_memory, window_data.data(),
|
||||
GetDataSize(current_config.trimming_format));
|
||||
memory.WriteBlock(transfer_memory, window_data.data(),
|
||||
GetDataSize(current_config.trimming_format));
|
||||
|
||||
if (!IsProcessorActive()) {
|
||||
StartProcessor();
|
||||
@@ -143,14 +143,19 @@ void ImageTransferProcessor::SetConfig(
|
||||
npad_device->SetCameraFormat(current_config.origin_format);
|
||||
}
|
||||
|
||||
void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
|
||||
void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_mem,
|
||||
Kernel::KProcess* owner) {
|
||||
transfer_memory = t_mem;
|
||||
transfer_memory_owner = owner;
|
||||
}
|
||||
|
||||
Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState(
|
||||
std::span<u8> data) const {
|
||||
if (transfer_memory_owner == nullptr)
|
||||
return processor_state;
|
||||
|
||||
const auto size = (std::min)(GetDataSize(current_config.trimming_format), data.size());
|
||||
system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size);
|
||||
transfer_memory_owner->GetMemory().ReadBlock(transfer_memory, data.data(), size);
|
||||
return processor_state;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -9,6 +12,10 @@
|
||||
#include "hid_core/irsensor/irs_types.h"
|
||||
#include "hid_core/irsensor/processor_base.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
@@ -39,7 +46,7 @@ public:
|
||||
void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config);
|
||||
|
||||
// Transfer memory where the image data will be stored
|
||||
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
|
||||
void SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner);
|
||||
|
||||
Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const;
|
||||
|
||||
@@ -75,5 +82,6 @@ private:
|
||||
|
||||
Core::System& system;
|
||||
Common::ProcessAddress transfer_memory{};
|
||||
Kernel::KProcess* transfer_memory_owner{};
|
||||
};
|
||||
} // namespace Service::IRS
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -6,6 +9,7 @@
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/frontend/emu_window.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/memory.h"
|
||||
#include "hid_core/frontend/emulated_console.h"
|
||||
#include "hid_core/frontend/emulated_devices.h"
|
||||
@@ -24,7 +28,7 @@ void SevenSixAxis::OnInit() {}
|
||||
void SevenSixAxis::OnRelease() {}
|
||||
|
||||
void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
if (!IsControllerActivated() || transfer_memory == 0) {
|
||||
if (!IsControllerActivated() || transfer_memory == 0 || transfer_memory_owner == nullptr) {
|
||||
seven_sixaxis_lifo.buffer_count = 0;
|
||||
seven_sixaxis_lifo.buffer_tail = 0;
|
||||
return;
|
||||
@@ -51,12 +55,13 @@ void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) {
|
||||
};
|
||||
|
||||
seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state);
|
||||
system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
|
||||
sizeof(seven_sixaxis_lifo));
|
||||
transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo,
|
||||
sizeof(seven_sixaxis_lifo));
|
||||
}
|
||||
|
||||
void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) {
|
||||
void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner) {
|
||||
transfer_memory = t_mem;
|
||||
transfer_memory_owner = owner;
|
||||
}
|
||||
|
||||
void SevenSixAxis::ResetTimestamp() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -9,6 +12,10 @@
|
||||
#include "hid_core/resources/controller_base.h"
|
||||
#include "hid_core/resources/ring_lifo.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
@@ -33,7 +40,7 @@ public:
|
||||
void OnUpdate(const Core::Timing::CoreTiming& core_timing) override;
|
||||
|
||||
// Called on InitializeSevenSixAxisSensor
|
||||
void SetTransferMemoryAddress(Common::ProcessAddress t_mem);
|
||||
void SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner);
|
||||
|
||||
// Called on ResetSevenSixAxisSensorTimestamp
|
||||
void ResetTimestamp();
|
||||
@@ -58,6 +65,7 @@ private:
|
||||
|
||||
SevenSixAxisState next_seven_sixaxis_state{};
|
||||
Common::ProcessAddress transfer_memory{};
|
||||
Kernel::KProcess* transfer_memory_owner{};
|
||||
Core::HID::EmulatedConsole* console = nullptr;
|
||||
|
||||
Core::System& system;
|
||||
|
||||
@@ -548,8 +548,18 @@ void TouchResource::OnTouchUpdate(s64 timestamp) {
|
||||
}
|
||||
|
||||
auto& touch_shared = applet_data->shared_memory_format->touch_screen;
|
||||
StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, bool(applet_data->flag.enable_touchscreen));
|
||||
touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state);
|
||||
|
||||
if (applet_data->flag.enable_touchscreen) {
|
||||
StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, true);
|
||||
touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state);
|
||||
} else {
|
||||
TouchScreenState denied{};
|
||||
denied.sampling_number = current_touch_state.sampling_number;
|
||||
denied.entry_count = 0;
|
||||
data.finger_map.finger_count = 0;
|
||||
data.finger_map.finger_ids = {};
|
||||
touch_shared.touch_screen_lifo.WriteNextEntry(denied);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,18 @@ void EmuThread::run() {
|
||||
QtCommon::system->Run();
|
||||
m_stopped.Reset();
|
||||
|
||||
m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; });
|
||||
m_should_run_cv.wait(lk, stop_token, [&] {
|
||||
return !m_should_run || m_pending_shader_cache_title.has_value();
|
||||
});
|
||||
|
||||
if (m_should_run && m_pending_shader_cache_title.has_value()) {
|
||||
const u64 program_id = *m_pending_shader_cache_title;
|
||||
m_pending_shader_cache_title.reset();
|
||||
|
||||
lk.unlock();
|
||||
this->ReloadDiskShaderCache(program_id);
|
||||
lk.lock();
|
||||
}
|
||||
} else {
|
||||
QtCommon::system->Pause();
|
||||
m_stopped.Set();
|
||||
@@ -67,6 +78,32 @@ void EmuThread::run() {
|
||||
QtCommon::system->ShutdownMainProcess();
|
||||
}
|
||||
|
||||
void EmuThread::ReloadDiskShaderCache(u64 program_id) {
|
||||
if (!Settings::values.use_disk_shader_cache.GetValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", program_id);
|
||||
|
||||
auto& system = *QtCommon::system;
|
||||
auto& gpu = system.GPU();
|
||||
|
||||
system.Pause();
|
||||
gpu.WaitForIdle();
|
||||
gpu.ObtainContext();
|
||||
|
||||
emit ShaderCacheReloadStarted();
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
system.Renderer().ReadRasterizer()->LoadDiskResources(program_id, m_stop_source.get_token(),
|
||||
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
|
||||
emit LoadProgress(stage, value, total);
|
||||
});
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);
|
||||
emit ShaderCacheReloadFinished();
|
||||
|
||||
gpu.ReleaseContext();
|
||||
}
|
||||
|
||||
// Unlock while emitting signals so that the main thread can
|
||||
// continue pumping events.
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QThread>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
@@ -63,9 +66,19 @@ public:
|
||||
m_stop_source.request_stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the disk shader cache be reloaded for a different title.
|
||||
*/
|
||||
void RequestDiskShaderCacheReload(u64 program_id) {
|
||||
std::unique_lock run_lk{m_should_run_mutex};
|
||||
m_pending_shader_cache_title = program_id;
|
||||
m_should_run_cv.notify_one();
|
||||
}
|
||||
|
||||
private:
|
||||
void EmulationPaused(std::unique_lock<std::mutex>& lk);
|
||||
void EmulationResumed(std::unique_lock<std::mutex>& lk);
|
||||
void ReloadDiskShaderCache(u64 program_id);
|
||||
|
||||
private:
|
||||
std::stop_source m_stop_source;
|
||||
@@ -73,6 +86,7 @@ private:
|
||||
std::condition_variable_any m_should_run_cv;
|
||||
Common::Event m_stopped;
|
||||
bool m_should_run{true};
|
||||
std::optional<u64> m_pending_shader_cache_title;
|
||||
|
||||
signals:
|
||||
/**
|
||||
@@ -94,4 +108,7 @@ signals:
|
||||
void DebugModeLeft();
|
||||
|
||||
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
|
||||
|
||||
void ShaderCacheReloadStarted();
|
||||
void ShaderCacheReloadFinished();
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ add_library(video_core STATIC
|
||||
control/channel_state_cache.h
|
||||
control/scheduler.cpp
|
||||
control/scheduler.h
|
||||
deferred_destruction_queue.h
|
||||
delayed_destruction_ring.h
|
||||
dirty_flags.cpp
|
||||
dirty_flags.h
|
||||
dma_pusher.cpp
|
||||
|
||||
@@ -31,77 +31,44 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
|
||||
immediately_free = (Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive);
|
||||
#endif
|
||||
if (!runtime.CanReportMemoryUsage()) {
|
||||
memory_budget = FALLBACK_MEMORY_BUDGET;
|
||||
minimum_memory = DEFAULT_EXPECTED_MEMORY;
|
||||
critical_memory = DEFAULT_CRITICAL_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
memory_budget = runtime.GetDeviceLocalMemory();
|
||||
const s64 device_local_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
|
||||
const s64 min_spacing_expected = device_local_memory - 1_GiB;
|
||||
const s64 min_spacing_critical = device_local_memory - 512_MiB;
|
||||
const s64 mem_threshold = (std::min)(device_local_memory, TARGET_THRESHOLD);
|
||||
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
|
||||
const s64 min_vacancy_critical = (2 * mem_threshold) / 10;
|
||||
minimum_memory = static_cast<u64>(
|
||||
(std::max)((std::min)(device_local_memory - min_vacancy_expected, min_spacing_expected),
|
||||
DEFAULT_EXPECTED_MEMORY));
|
||||
critical_memory = static_cast<u64>(
|
||||
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
|
||||
DEFAULT_CRITICAL_MEMORY));
|
||||
}
|
||||
|
||||
template <class P>
|
||||
BufferCache<P>::~BufferCache() = default;
|
||||
|
||||
template <class P>
|
||||
u64 BufferCache<P>::DeviceUsage(bool force_refresh) {
|
||||
if (!runtime.CanReportAllocationUsage()) {
|
||||
return total_used_memory;
|
||||
}
|
||||
if (force_refresh || usage_refresh_countdown == 0) {
|
||||
cached_device_usage = runtime.GetDeviceAllocationUsage();
|
||||
usage_refresh_countdown = USAGE_REFRESH_INTERVAL;
|
||||
} else {
|
||||
--usage_refresh_countdown;
|
||||
}
|
||||
return cached_device_usage;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
u64 BufferCache<P>::ReclaimMemory(u64 target_bytes, bool allow_download) {
|
||||
if (target_bytes == 0 || in_reclaim) {
|
||||
return 0;
|
||||
}
|
||||
in_reclaim = true;
|
||||
sentenced_buffers.Reclaim(runtime.CompletedSyncPoint());
|
||||
u64 freed = 0;
|
||||
const auto clean_up = [&](BufferId buffer_id) {
|
||||
if (freed >= target_bytes) {
|
||||
void BufferCache<P>::RunGarbageCollector() {
|
||||
const bool aggressive_gc = total_used_memory >= critical_memory;
|
||||
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
|
||||
int num_iterations = aggressive_gc ? 64 : 32;
|
||||
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
|
||||
if (num_iterations == 0) {
|
||||
return true;
|
||||
}
|
||||
--num_iterations;
|
||||
auto& buffer = slot_buffers[buffer_id];
|
||||
if (!allow_download && IsRegionGpuModified(buffer.CpuAddr(), buffer.SizeBytes())) {
|
||||
return false;
|
||||
}
|
||||
const u64 buffer_bytes = Common::AlignUp(buffer.SizeBytes(), 1024);
|
||||
DownloadBufferMemory(buffer);
|
||||
DeleteBuffer(buffer_id);
|
||||
freed += buffer_bytes;
|
||||
return false;
|
||||
};
|
||||
const u64 cold_tick =
|
||||
frame_tick > RECLAIM_GUARD_FRAMES ? frame_tick - RECLAIM_GUARD_FRAMES : 0;
|
||||
lru_cache.ForEachItemBelow(cold_tick, clean_up);
|
||||
if (freed < target_bytes) {
|
||||
lru_cache.ForEachItemBelow(frame_tick > 0 ? frame_tick - 1 : 0, clean_up);
|
||||
}
|
||||
sentenced_buffers.Reclaim(runtime.CompletedSyncPoint());
|
||||
in_reclaim = false;
|
||||
usage_refresh_countdown = 0;
|
||||
reclaim_stalled = freed == 0;
|
||||
return freed;
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::EnsureHeadroom(bool allow_download) {
|
||||
if (reclaim_stalled) {
|
||||
return;
|
||||
}
|
||||
const u64 limit = memory_budget > RECLAIM_HEADROOM ? memory_budget - RECLAIM_HEADROOM : 0;
|
||||
const u64 usage = DeviceUsage(false);
|
||||
if (usage <= limit) {
|
||||
return;
|
||||
}
|
||||
const u64 target = (limit / 100) * RECLAIM_TARGET_PERCENT;
|
||||
ReclaimMemory((std::min)(usage - target, total_used_memory), allow_download);
|
||||
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
|
||||
}
|
||||
|
||||
template <class P>
|
||||
@@ -129,11 +96,15 @@ void BufferCache<P>::TickFrame() {
|
||||
const bool skip_preferred = hits * 256 < shots * 251;
|
||||
channel_state->uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
|
||||
|
||||
usage_refresh_countdown = 0;
|
||||
reclaim_stalled = false;
|
||||
EnsureHeadroom(true);
|
||||
// If we can obtain the memory info, use it instead of the estimate.
|
||||
if (runtime.CanReportMemoryUsage()) {
|
||||
total_used_memory = runtime.GetDeviceMemoryUsage();
|
||||
}
|
||||
if (total_used_memory >= minimum_memory) {
|
||||
RunGarbageCollector();
|
||||
}
|
||||
++frame_tick;
|
||||
sentenced_buffers.Reclaim(runtime.CompletedSyncPoint());
|
||||
delayed_destruction_ring.Tick();
|
||||
|
||||
for (auto& buffer : async_buffers_death_ring) {
|
||||
runtime.FreeDeferredStagingBuffer(buffer);
|
||||
@@ -1605,7 +1576,6 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
|
||||
|
||||
template <class P>
|
||||
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size) {
|
||||
EnsureHeadroom(false);
|
||||
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
||||
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
||||
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
||||
@@ -1643,7 +1613,7 @@ void BufferCache<P>::ChangeRegister(BufferId buffer_id) {
|
||||
total_used_memory += Common::AlignUp(size, 1024);
|
||||
buffer.setLRUID(lru_cache.Insert(buffer_id, frame_tick));
|
||||
} else {
|
||||
total_used_memory -= std::min<u64>(total_used_memory, Common::AlignUp(size, 1024));
|
||||
total_used_memory -= Common::AlignUp(size, 1024);
|
||||
lru_cache.Free(buffer.getLRUID());
|
||||
}
|
||||
const DAddr device_addr_begin = buffer.CpuAddr();
|
||||
@@ -1902,7 +1872,7 @@ void BufferCache<P>::DeleteBuffer(BufferId buffer_id, bool do_not_mark) {
|
||||
#ifdef YUZU_LEGACY
|
||||
if (!do_not_mark || !immediately_free)
|
||||
#endif
|
||||
sentenced_buffers.Push(std::move(slot_buffers[buffer_id]), runtime.CurrentSyncPoint());
|
||||
delayed_destruction_ring.Push(std::move(slot_buffers[buffer_id]));
|
||||
|
||||
slot_buffers.erase(buffer_id);
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <deque>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -31,7 +30,7 @@
|
||||
#include "common/slot_vector.h"
|
||||
#include "video_core/buffer_cache/buffer_base.h"
|
||||
#include "video_core/control/channel_state_cache.h"
|
||||
#include "video_core/deferred_destruction_queue.h"
|
||||
#include "video_core/delayed_destruction_ring.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
@@ -183,15 +182,13 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
|
||||
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = P::USE_MEMORY_MAPS_FOR_UPLOADS;
|
||||
|
||||
#ifdef YUZU_LEGACY
|
||||
static constexpr u64 RECLAIM_HEADROOM = 384_MiB;
|
||||
static constexpr s64 TARGET_THRESHOLD = 3_GiB;
|
||||
#else
|
||||
static constexpr u64 RECLAIM_HEADROOM = 512_MiB;
|
||||
static constexpr s64 TARGET_THRESHOLD = 4_GiB;
|
||||
#endif
|
||||
|
||||
static constexpr u64 FALLBACK_MEMORY_BUDGET = 2_GiB;
|
||||
static constexpr u32 USAGE_REFRESH_INTERVAL = 16;
|
||||
static constexpr u64 RECLAIM_GUARD_FRAMES = 8;
|
||||
static constexpr u64 RECLAIM_TARGET_PERCENT = 88;
|
||||
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
|
||||
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
|
||||
|
||||
// Debug Flags.
|
||||
|
||||
@@ -218,8 +215,6 @@ public:
|
||||
|
||||
void TickFrame();
|
||||
|
||||
u64 ReclaimMemory(u64 target_bytes, bool allow_download);
|
||||
|
||||
void WriteMemory(DAddr device_addr, u64 size);
|
||||
|
||||
void CachedWriteMemory(DAddr device_addr, u64 size);
|
||||
@@ -363,9 +358,7 @@ private:
|
||||
((device_addr + size) & ~Core::DEVICE_PAGEMASK);
|
||||
}
|
||||
|
||||
u64 DeviceUsage(bool force_refresh);
|
||||
|
||||
void EnsureHeadroom(bool allow_download);
|
||||
void RunGarbageCollector();
|
||||
|
||||
void BindHostIndexBuffer();
|
||||
|
||||
@@ -482,7 +475,12 @@ private:
|
||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||
|
||||
Common::SlotVector<Buffer> slot_buffers;
|
||||
DeferredDestructionQueue<Buffer> sentenced_buffers;
|
||||
#ifdef YUZU_LEGACY
|
||||
static constexpr size_t TICKS_TO_DESTROY = 6;
|
||||
#else
|
||||
static constexpr size_t TICKS_TO_DESTROY = 8;
|
||||
#endif
|
||||
DelayedDestructionRing<Buffer, TICKS_TO_DESTROY> delayed_destruction_ring;
|
||||
|
||||
const Tegra::Engines::Maxwell3D::DrawManager::IndirectParams* current_draw_indirect{};
|
||||
|
||||
@@ -517,11 +515,8 @@ private:
|
||||
Common::LeastRecentlyUsedCache<LRUItemParams> lru_cache;
|
||||
u64 frame_tick = 0;
|
||||
u64 total_used_memory = 0;
|
||||
u64 memory_budget = 0;
|
||||
u64 cached_device_usage = 0;
|
||||
u32 usage_refresh_countdown = 0;
|
||||
bool in_reclaim = false;
|
||||
bool reclaim_stalled = false;
|
||||
u64 minimum_memory = 0;
|
||||
u64 critical_memory = 0;
|
||||
BufferId inline_buffer_id;
|
||||
#ifdef YUZU_LEGACY
|
||||
bool immediately_free = false;
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/container/deque.hpp>
|
||||
#include <boost/container/options.hpp>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
template <typename T>
|
||||
class DeferredDestructionQueue {
|
||||
public:
|
||||
void Push(T&& object, u64 sync_point) {
|
||||
entries.emplace_back(std::move(object), sync_point);
|
||||
}
|
||||
|
||||
void Reclaim(u64 completed_sync_point) {
|
||||
while (!entries.empty() && entries.front().sync_point <= completed_sync_point) {
|
||||
entries.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
entries.clear();
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t Size() const noexcept {
|
||||
return entries.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Empty() const noexcept {
|
||||
return entries.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
Entry(T&& object_, u64 sync_point_) noexcept
|
||||
: object{std::move(object_)}, sync_point{sync_point_} {}
|
||||
|
||||
T object;
|
||||
u64 sync_point;
|
||||
};
|
||||
|
||||
using EntryDequeOptions =
|
||||
boost::container::deque_options<boost::container::block_size<8u>>::type;
|
||||
|
||||
boost::container::deque<Entry, void, EntryDequeOptions> entries;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
@@ -0,0 +1,34 @@
|
||||
// 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
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
/// Container to push objects to be destroyed a few ticks in the future
|
||||
template <typename T, size_t TICKS_TO_DESTROY>
|
||||
class DelayedDestructionRing {
|
||||
public:
|
||||
void Tick() {
|
||||
index = (index + 1) % TICKS_TO_DESTROY;
|
||||
elements[index].clear();
|
||||
}
|
||||
|
||||
void Push(T&& object) {
|
||||
elements[index].push_back(std::move(object));
|
||||
}
|
||||
|
||||
private:
|
||||
size_t index = 0;
|
||||
std::array<std::vector<T>, TICKS_TO_DESTROY> elements;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/settings.h"
|
||||
#include "common/thread.h"
|
||||
#include "video_core/deferred_destruction_queue.h"
|
||||
#include "video_core/delayed_destruction_ring.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/host1x/host1x.h"
|
||||
#include "video_core/host1x/syncpoint_manager.h"
|
||||
@@ -50,8 +50,7 @@ public:
|
||||
/// Notify the fence manager about a new frame
|
||||
void TickFrame() {
|
||||
std::unique_lock lock(ring_guard);
|
||||
++retire_tick;
|
||||
sentenced_fences.Reclaim(retire_tick > RETIRE_DELAY ? retire_tick - RETIRE_DELAY : 0);
|
||||
delayed_destruction_ring.Tick();
|
||||
}
|
||||
|
||||
// Unlike other fences, this one doesn't
|
||||
@@ -187,7 +186,7 @@ private:
|
||||
}
|
||||
{
|
||||
std::unique_lock lock(ring_guard);
|
||||
sentenced_fences.Push(std::move(current_fence), retire_tick);
|
||||
delayed_destruction_ring.Push(std::move(current_fence));
|
||||
}
|
||||
fences.pop();
|
||||
}
|
||||
@@ -220,7 +219,7 @@ private:
|
||||
}
|
||||
{
|
||||
std::unique_lock lock(ring_guard);
|
||||
sentenced_fences.Push(std::move(current_fence), retire_tick);
|
||||
delayed_destruction_ring.Push(std::move(current_fence));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,9 +264,7 @@ private:
|
||||
|
||||
std::jthread fence_thread;
|
||||
|
||||
static constexpr u64 RETIRE_DELAY = 8;
|
||||
u64 retire_tick = 1;
|
||||
DeferredDestructionQueue<TFence> sentenced_fences;
|
||||
DelayedDestructionRing<TFence, 8> delayed_destruction_ring;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
@@ -1,11 +1,35 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/framebuffer_config.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
std::span<const FramebufferConfig> FilterLayerStack(
|
||||
std::span<const FramebufferConfig> layers, Service::Nvnflinger::LayerStackId stack,
|
||||
std::vector<FramebufferConfig>& scratch) {
|
||||
const u32 bit = Service::Nvnflinger::LayerStackBit(stack);
|
||||
|
||||
if (std::ranges::all_of(layers,
|
||||
[bit](const auto& layer) { return (layer.layer_stack_mask & bit) != 0; }))
|
||||
return layers;
|
||||
|
||||
scratch.clear();
|
||||
for (const auto& layer : layers) {
|
||||
if ((layer.layer_stack_mask & bit) != 0) {
|
||||
scratch.push_back(layer);
|
||||
}
|
||||
}
|
||||
|
||||
return scratch;
|
||||
}
|
||||
|
||||
Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
|
||||
u32 texture_height) {
|
||||
f32 left, top, right, bottom;
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "core/hle/service/nvnflinger/buffer_transform_flags.h"
|
||||
#include "core/hle/service/nvnflinger/hwc_layer.h"
|
||||
#include "core/hle/service/nvnflinger/pixel_format.h"
|
||||
#include "core/hle/service/nvnflinger/ui/fence.h"
|
||||
|
||||
@@ -30,9 +37,17 @@ struct FramebufferConfig {
|
||||
Service::android::BufferTransformFlags transform_flags{};
|
||||
Common::Rectangle<int> crop_rect{};
|
||||
BlendMode blending{};
|
||||
u32 layer_stack_mask{Service::Nvnflinger::DefaultLayerStackMask};
|
||||
};
|
||||
|
||||
Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width,
|
||||
u32 texture_height);
|
||||
|
||||
/**
|
||||
* Returns the subset of layers belonging to a stack.
|
||||
*/
|
||||
std::span<const FramebufferConfig> FilterLayerStack(std::span<const FramebufferConfig> layers,
|
||||
Service::Nvnflinger::LayerStackId stack,
|
||||
std::vector<FramebufferConfig>& scratch);
|
||||
|
||||
} // namespace Tegra
|
||||
|
||||
@@ -130,6 +130,12 @@ struct GPU::Impl {
|
||||
sync_request_cv.wait(lck, [this, fence] { return CurrentSyncRequestFence() >= fence; });
|
||||
}
|
||||
|
||||
void WaitForIdle() {
|
||||
const u64 fence = RequestSyncOperation([] {});
|
||||
gpu_thread.TickGPU(is_async);
|
||||
WaitForSyncOperation(fence);
|
||||
}
|
||||
|
||||
/// Tick pending requests within the GPU.
|
||||
void TickWork() {
|
||||
std::unique_lock lck{sync_request_mutex};
|
||||
@@ -456,6 +462,10 @@ void GPU::NotifyShutdown() {
|
||||
impl->NotifyShutdown();
|
||||
}
|
||||
|
||||
void GPU::WaitForIdle() {
|
||||
impl->WaitForIdle();
|
||||
}
|
||||
|
||||
void GPU::ObtainContext() {
|
||||
impl->ObtainContext();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
@@ -168,6 +168,8 @@ public:
|
||||
|
||||
void WaitForSyncOperation(u64 fence);
|
||||
|
||||
void WaitForIdle();
|
||||
|
||||
/// Tick pending requests within the GPU.
|
||||
void TickWork();
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Core::Fronten
|
||||
thread = std::jthread([&](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("GPU");
|
||||
Common::SetCurrentThreadPriority(Common::ThreadPriority::Critical);
|
||||
Common::SetCurrentThreadToPerformanceCores();
|
||||
system.RegisterHostThread();
|
||||
|
||||
auto current_context = context.Acquire();
|
||||
|
||||
@@ -32,7 +32,6 @@ set(SHADER_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_msaa_to_non_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.comp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_non_msaa_to_msaa_depth.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/convert_s8d24_to_abgr8.frag
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/full_screen_triangle.vert
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/fxaa.frag
|
||||
|
||||
@@ -1384,7 +1384,11 @@ void DecompressBlock(ivec3 coord) {
|
||||
p = Cf / 65535.0f;
|
||||
}
|
||||
|
||||
#ifdef VULKAN
|
||||
imageStore(dest_image, coord + ivec3(i, j, 0), p.gbar);
|
||||
#else
|
||||
imageStore(dest_image, coord + ivec3(i, j, 0), clamp(p, 0.0f, 1.0f).gbar);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#version 450 core
|
||||
|
||||
layout(binding = 0) uniform sampler2D img_in;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
ivec2 dst_offset;
|
||||
ivec2 src_offset;
|
||||
ivec2 scale;
|
||||
};
|
||||
|
||||
void main() {
|
||||
const ivec2 msaa_coord = ivec2(gl_FragCoord.xy) - dst_offset;
|
||||
const ivec2 sample_offset = ivec2(gl_SampleID % scale.x, gl_SampleID / scale.x);
|
||||
const ivec2 coord = msaa_coord * scale + sample_offset + src_offset;
|
||||
gl_FragDepth = texelFetch(img_in, coord, 0).r;
|
||||
}
|
||||
+83
-79
@@ -417,14 +417,6 @@ void HLE_TransformFeedbackSetup::Execute(Core::System& system, Engines::Maxwell3
|
||||
default: return std::monostate{};
|
||||
}
|
||||
}
|
||||
[[nodiscard]] inline bool CanBeHLEProgram(u64 hash) noexcept {
|
||||
switch (hash) {
|
||||
#define HLE_MACRO_ELEM(HASH, TY, VAL) case HASH: return true;
|
||||
HLE_MACRO_LIST
|
||||
#undef HLE_MACRO_ELEM
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MacroInterpreterImpl::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, std::span<const u32> params, u32 method) {
|
||||
Reset();
|
||||
@@ -1345,80 +1337,92 @@ static void Dump(u64 hash, std::span<const u32> code, bool decompiled = false) {
|
||||
macro_file.write(reinterpret_cast<const char*>(code.data()), code.size_bytes());
|
||||
}
|
||||
|
||||
void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method, std::span<const u32> parameters) {
|
||||
auto const execute_variant = [&system, &maxwell3d, ¶meters, method](AnyCachedMacro& acm) {
|
||||
if (auto a = std::get_if<HLE_DrawArraysIndirect>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_DrawIndexedIndirect>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_MultiDrawIndexedIndirectCount>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_MultiLayerClear>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_C713C83D8F63CCF3>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_D7333D26E0A93EDE>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_BindShader>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_SetRasterBoundingBox>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_ClearConstBuffer>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_ClearMemory>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_TransformFeedbackSetup>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<HLE_DrawIndirectByteCount>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<MacroInterpreterImpl>(&acm))
|
||||
return a->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto a = std::get_if<std::unique_ptr<DynamicCachedMacro>>(&acm))
|
||||
return a->get()->Execute(system, maxwell3d, parameters, method);
|
||||
};
|
||||
if (auto const it = macro_cache.find(method); it != macro_cache.end()) {
|
||||
auto& ci = it->second;
|
||||
if (!CanBeHLEProgram(ci.hash) || Settings::values.disable_macro_hle)
|
||||
maxwell3d.RefreshParameters(); //LLE must reload parameters
|
||||
execute_variant(ci.program);
|
||||
} else {
|
||||
// Macro not compiled, check if it's uploaded and if so, compile it
|
||||
std::optional<u32> mid_method;
|
||||
const auto macro_code = uploaded_macro_code.find(method);
|
||||
if (macro_code == uploaded_macro_code.end()) {
|
||||
for (const auto& [method_base, code] : uploaded_macro_code) {
|
||||
if (method >= method_base && (method - method_base) < code.size()) {
|
||||
mid_method = method_base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mid_method.has_value()) {
|
||||
ASSERT_MSG(false, "Macro 0x{0:x} was not uploaded", method);
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto& ci = macro_cache[method];
|
||||
if (mid_method) {
|
||||
const auto& macro_cached = uploaded_macro_code[mid_method.value()];
|
||||
const auto rebased_method = method - mid_method.value();
|
||||
auto& code = uploaded_macro_code[method];
|
||||
code.resize(macro_cached.size() - rebased_method);
|
||||
std::memcpy(code.data(), macro_cached.data() + rebased_method, code.size() * sizeof(u32));
|
||||
ci.hash = Common::HashValue(code);
|
||||
ci.program = Compile(system, maxwell3d, code);
|
||||
} else {
|
||||
ci.program = Compile(system, maxwell3d, macro_code->second);
|
||||
ci.hash = Common::HashValue(macro_code->second);
|
||||
}
|
||||
if (CanBeHLEProgram(ci.hash) && !Settings::values.disable_macro_hle) {
|
||||
ci.program = GetHLEProgram(ci.hash);
|
||||
} else {
|
||||
void MacroEngine::Execute(Core::System& system, Engines::Maxwell3D& maxwell3d, u32 method,
|
||||
std::span<const u32> parameters) {
|
||||
const auto execute_variant = [&system, &maxwell3d, ¶meters,
|
||||
method](AnyCachedMacro& cached) {
|
||||
if (std::holds_alternative<MacroInterpreterImpl>(cached) ||
|
||||
std::holds_alternative<std::unique_ptr<DynamicCachedMacro>>(cached) ||
|
||||
Settings::values.disable_macro_hle) {
|
||||
maxwell3d.RefreshParameters();
|
||||
}
|
||||
execute_variant(ci.program);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(ci.hash, macro_code->second, !std::holds_alternative<std::monostate>(ci.program));
|
||||
|
||||
if (auto program = std::get_if<HLE_DrawArraysIndirect>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_DrawIndexedIndirect>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_MultiDrawIndexedIndirectCount>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_MultiLayerClear>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_C713C83D8F63CCF3>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_D7333D26E0A93EDE>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_BindShader>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_SetRasterBoundingBox>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_ClearConstBuffer>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_ClearMemory>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_TransformFeedbackSetup>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<HLE_DrawIndirectByteCount>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<MacroInterpreterImpl>(&cached))
|
||||
return program->Execute(system, maxwell3d, parameters, method);
|
||||
if (auto program = std::get_if<std::unique_ptr<DynamicCachedMacro>>(&cached))
|
||||
return program->get()->Execute(system, maxwell3d, parameters, method);
|
||||
|
||||
UNREACHABLE();
|
||||
};
|
||||
if (auto const it = macro_cache.find(method); it != macro_cache.end()) {
|
||||
execute_variant(it->second.program);
|
||||
return;
|
||||
}
|
||||
|
||||
// Macro not compiled, check if it's uploaded and if so, compile it
|
||||
std::span<const u32> code;
|
||||
auto macro_code = uploaded_macro_code.find(method);
|
||||
if (macro_code == uploaded_macro_code.end()) {
|
||||
std::optional<u32> mid_method;
|
||||
for (const auto& [method_base, uploaded_code] : uploaded_macro_code) {
|
||||
if (method >= method_base && (method - method_base) < uploaded_code.size()) {
|
||||
mid_method = method_base;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mid_method) {
|
||||
ASSERT_MSG(false, "Macro 0x{0:x} was not uploaded", method);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto source = uploaded_macro_code.find(*mid_method);
|
||||
ASSERT(source != uploaded_macro_code.end());
|
||||
const auto rebased_method = method - *mid_method;
|
||||
std::vector<u32> rebased_code(source->second.begin() + rebased_method,
|
||||
source->second.end());
|
||||
const auto [it, inserted] = uploaded_macro_code.emplace(method, std::move(rebased_code));
|
||||
ASSERT(inserted);
|
||||
code = it->second;
|
||||
} else {
|
||||
code = macro_code->second;
|
||||
}
|
||||
|
||||
auto& ci = macro_cache[method];
|
||||
ci.hash = Common::HashRange(code.begin(), code.end());
|
||||
if (!Settings::values.disable_macro_hle) {
|
||||
ci.program = GetHLEProgram(ci.hash);
|
||||
}
|
||||
if (std::holds_alternative<std::monostate>(ci.program)) {
|
||||
ci.program = Compile(system, maxwell3d, code);
|
||||
}
|
||||
|
||||
execute_variant(ci.program);
|
||||
if (Settings::values.dump_macros) {
|
||||
Dump(ci.hash, code, !std::holds_alternative<std::monostate>(ci.program));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ bool RendererBase::IsScreenshotPending() const {
|
||||
}
|
||||
|
||||
void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callback,
|
||||
const Layout::FramebufferLayout& layout) {
|
||||
const Layout::FramebufferLayout& layout,
|
||||
Service::Nvnflinger::LayerStackId layer_stack) {
|
||||
if (renderer_settings.screenshot_requested) {
|
||||
LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request");
|
||||
return;
|
||||
@@ -48,6 +49,7 @@ void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callb
|
||||
renderer_settings.screenshot_bits = data;
|
||||
renderer_settings.screenshot_complete_callback = async_callback;
|
||||
renderer_settings.screenshot_framebuffer_layout = layout;
|
||||
renderer_settings.screenshot_layer_stack = layer_stack;
|
||||
renderer_settings.screenshot_requested = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -26,6 +29,7 @@ struct RendererSettings {
|
||||
void* screenshot_bits{};
|
||||
std::function<void(bool)> screenshot_complete_callback;
|
||||
Layout::FramebufferLayout screenshot_framebuffer_layout;
|
||||
Service::Nvnflinger::LayerStackId screenshot_layer_stack{Service::Nvnflinger::LayerStackId::Default};
|
||||
};
|
||||
|
||||
class RendererBase {
|
||||
@@ -88,9 +92,11 @@ public:
|
||||
/// Returns true if a screenshot is being processed
|
||||
bool IsScreenshotPending() const;
|
||||
|
||||
/// Request a screenshot of the next frame
|
||||
/// Request a screenshot of the next frame.
|
||||
void RequestScreenshot(void* data, std::function<void(bool)> callback,
|
||||
const Layout::FramebufferLayout& layout);
|
||||
const Layout::FramebufferLayout& layout,
|
||||
Service::Nvnflinger::LayerStackId layer_stack =
|
||||
Service::Nvnflinger::LayerStackId::Default);
|
||||
|
||||
protected:
|
||||
Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle.
|
||||
|
||||
@@ -93,17 +93,7 @@ public:
|
||||
void PostCopyBarrier();
|
||||
void Finish();
|
||||
|
||||
void TickFrame(Common::SlotVector<Buffer>&) noexcept {
|
||||
++sync_point;
|
||||
}
|
||||
|
||||
u64 CurrentSyncPoint() const noexcept {
|
||||
return sync_point;
|
||||
}
|
||||
|
||||
u64 CompletedSyncPoint() const noexcept {
|
||||
return sync_point > SYNC_POINT_DELAY ? sync_point - SYNC_POINT_DELAY : 0;
|
||||
}
|
||||
void TickFrame(Common::SlotVector<Buffer>&) noexcept {}
|
||||
|
||||
void ClearBuffer(Buffer& dest_buffer, u32 offset, size_t size, u32 value);
|
||||
|
||||
@@ -138,14 +128,6 @@ public:
|
||||
|
||||
u64 GetDeviceMemoryUsage() const;
|
||||
|
||||
u64 GetDeviceAllocationUsage() const {
|
||||
return GetDeviceMemoryUsage();
|
||||
}
|
||||
|
||||
bool CanReportAllocationUsage() const {
|
||||
return device.CanReportMemoryUsage();
|
||||
}
|
||||
|
||||
void BindFastUniformBuffer(size_t stage, u32 binding_index, u32 size) {
|
||||
const GLuint handle = fast_uniforms[stage][binding_index].handle;
|
||||
const GLsizeiptr gl_size = static_cast<GLsizeiptr>(size);
|
||||
@@ -231,13 +213,9 @@ private:
|
||||
GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV,
|
||||
};
|
||||
|
||||
static constexpr u64 SYNC_POINT_DELAY = 8;
|
||||
|
||||
const Device& device;
|
||||
StagingBufferPool& staging_buffer_pool;
|
||||
|
||||
u64 sync_point = 1;
|
||||
|
||||
bool has_fast_buffer_sub_data = false;
|
||||
bool use_assembly_shaders = false;
|
||||
bool has_unified_vertex_buffers = false;
|
||||
|
||||
@@ -87,14 +87,6 @@ public:
|
||||
|
||||
u64 GetDeviceMemoryUsage() const;
|
||||
|
||||
u64 GetDeviceAllocationUsage() const {
|
||||
return GetDeviceMemoryUsage();
|
||||
}
|
||||
|
||||
bool CanReportAllocationUsage() const {
|
||||
return device.CanReportMemoryUsage();
|
||||
}
|
||||
|
||||
bool CanReportMemoryUsage() const {
|
||||
return device.CanReportMemoryUsage();
|
||||
}
|
||||
@@ -147,19 +139,7 @@ public:
|
||||
|
||||
bool HasNativeASTC() const noexcept;
|
||||
|
||||
void TickFrame() {
|
||||
++sync_point;
|
||||
}
|
||||
|
||||
u64 CurrentSyncPoint() const noexcept {
|
||||
return sync_point;
|
||||
}
|
||||
|
||||
u64 CompletedSyncPoint() const noexcept {
|
||||
return sync_point > SYNC_POINT_DELAY ? sync_point - SYNC_POINT_DELAY : 0;
|
||||
}
|
||||
|
||||
void WaitSyncPoint(u64) {}
|
||||
void TickFrame() {}
|
||||
|
||||
StateTracker& GetStateTracker() {
|
||||
return state_tracker;
|
||||
@@ -194,9 +174,6 @@ private:
|
||||
std::array<OGLFramebuffer, 4> rescale_read_fbos;
|
||||
const Settings::ResolutionScalingInfo& resolution;
|
||||
u64 device_access_memory;
|
||||
|
||||
static constexpr u64 SYNC_POINT_DELAY = 8;
|
||||
u64 sync_point = 1;
|
||||
};
|
||||
|
||||
class Image : public VideoCommon::ImageBase {
|
||||
@@ -393,7 +370,6 @@ struct TextureCacheParams {
|
||||
static constexpr bool HAS_EMULATED_COPIES = true;
|
||||
static constexpr bool HAS_DEVICE_MEMORY_INFO = true;
|
||||
static constexpr bool IMPLEMENTS_ASYNC_DOWNLOADS = true;
|
||||
static constexpr bool HAS_TIMELINE_SYNC_POINTS = false;
|
||||
|
||||
using Runtime = OpenGL::TextureCacheRuntime;
|
||||
using Image = OpenGL::Image;
|
||||
|
||||
@@ -199,7 +199,10 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig>
|
||||
return;
|
||||
}
|
||||
|
||||
RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout,
|
||||
const auto screenshot_layers = Tegra::FilterLayerStack(
|
||||
framebuffers, renderer_settings.screenshot_layer_stack, screenshot_layer_scratch);
|
||||
|
||||
RenderToBuffer(screenshot_layers, renderer_settings.screenshot_framebuffer_layout,
|
||||
renderer_settings.screenshot_bits);
|
||||
|
||||
renderer_settings.screenshot_complete_callback(true);
|
||||
@@ -208,6 +211,12 @@ void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig>
|
||||
|
||||
void RendererOpenGL::RenderAppletCaptureLayer(
|
||||
std::span<const Tegra::FramebufferConfig> framebuffers) {
|
||||
const auto capture_layers = Tegra::FilterLayerStack(
|
||||
framebuffers, Service::Nvnflinger::LayerStackId::LastFrame, applet_capture_layers);
|
||||
|
||||
if (capture_layers.empty())
|
||||
return;
|
||||
|
||||
GLint old_read_fb;
|
||||
GLint old_draw_fb;
|
||||
glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
|
||||
@@ -217,7 +226,7 @@ void RendererOpenGL::RenderAppletCaptureLayer(
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
|
||||
capture_renderbuffer.handle);
|
||||
|
||||
blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true);
|
||||
blit_applet->DrawScreen(capture_layers, VideoCore::Capture::Layout, true);
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
|
||||
@@ -61,6 +61,9 @@ private:
|
||||
void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||
void RenderAppletCaptureLayer(std::span<const Tegra::FramebufferConfig> framebuffers);
|
||||
|
||||
std::vector<Tegra::FramebufferConfig> applet_capture_layers;
|
||||
std::vector<Tegra::FramebufferConfig> screenshot_layer_scratch;
|
||||
|
||||
Core::Frontend::EmuWindow& emu_window;
|
||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||
Tegra::GPU& gpu;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user