mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-14 12:56:09 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ddd735922e | |||
| 25e6db0b36 |
+1
@@ -30,6 +30,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
|
||||
ENABLE_BUFFER_HISTORY("enable_buffer_history"),
|
||||
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
|
||||
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
|
||||
SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
|
||||
BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
|
||||
RENDERER_DEBUG("debug"),
|
||||
|
||||
+7
@@ -808,6 +808,13 @@ abstract class SettingsItem(
|
||||
descriptionId = R.string.enable_buffer_history_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
|
||||
titleId = R.string.enable_gpu_buffer_readback,
|
||||
descriptionId = R.string.enable_gpu_buffer_readback_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
|
||||
|
||||
+1
@@ -293,6 +293,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
|
||||
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
|
||||
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
|
||||
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
|
||||
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
|
||||
|
||||
add(HeaderSetting(R.string.hacks))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -499,6 +499,8 @@
|
||||
<string name="renderer_reactive_flushing_description">يحسن دقة العرض في بعض الألعاب على حساب الأداء.</string>
|
||||
<string name="enable_buffer_history">تمكين سجل التخزين المؤقت</string>
|
||||
<string name="enable_buffer_history_description">يُتيح هذا الخيار الوصول إلى حالات التخزين المؤقت السابقة. وقد يُحسّن جودة العرض وثبات الأداء في بعض الألعاب.</string>
|
||||
<string name="enable_gpu_buffer_readback">تفعيل قراءة مخزن وحدة معالجة الرسومات</string>
|
||||
<string name="enable_gpu_buffer_readback_description">يحافظ هذا النظام على بيانات المخزن المؤقت المُعدّلة بواسطة وحدة معالجة الرسومات عن طريق قراءتها مرة أخرى قبل التحميل. تتطلب بعض الألعاب ذلك لعرض بعض التأثيرات بشكل صحيح. قد يُسبب ذلك مشاكل إذا لم يتمكن الجهاز من التعامل مع عبء العمل الإضافي.</string>
|
||||
<string name="use_optimized_vertex_buffers">مخازن الرؤوس المُحسّنة</string>
|
||||
<string name="use_optimized_vertex_buffers_description">يُتيح ربطًا مُحسَّنًا لمخازن الرؤوس لتحسين الأداء. يتطلب برامج تشغيل Mesa 26.0+ Turnip/ برامج تشغيل QCOM. قد يتعطل على برامج تشغيل Turnip القديمة (25.3 وما دون).</string>
|
||||
|
||||
|
||||
@@ -491,6 +491,8 @@
|
||||
<string name="renderer_reactive_flushing_description">Mejora la precisión de renderizado en algunos juegos, pero reduce el rendimiento.</string>
|
||||
<string name="enable_buffer_history">Activar el historial del búfer</string>
|
||||
<string name="enable_buffer_history_description">Permite el acceso al estado del búfer anterior. Esta opción puede mejorar la calidad de renderizado y la consistencia en el rendimiento de algunos juegos.</string>
|
||||
<string name="enable_gpu_buffer_readback">Activar la lectura del buffer de la GPU</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Conserva los datos del búfer modificados por la GPU leyéndolos antes de subirlos.\nAlgunos juegos requieren esto para renderizar correctamente ciertos efectos.\nPuede causar problemas si el hardware no puede soportar la carga de trabajo adicional.</string>
|
||||
<string name="use_optimized_vertex_buffers">Búferes de vértices optimizados</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Permite la optimización del enlace del búfer de vértices para un mejor rendimiento. Requiere controladores Mesa 26.0+ Turnip/ controladores QCOM. Fallará con controladores Turnip más antiguos (versión 25.3 o inferior).</string>
|
||||
|
||||
|
||||
@@ -489,6 +489,8 @@
|
||||
<string name="renderer_reactive_flushing_description">通过牺牲性能来提升某些游戏的渲染精度。</string>
|
||||
<string name="enable_buffer_history">启用缓冲区历史</string>
|
||||
<string name="enable_buffer_history_description">启用对先前缓冲区状态的访问。此选项可在某些游戏中提升渲染质量并保持性能的一致性。</string>
|
||||
<string name="enable_gpu_buffer_readback">启用 GPU 缓冲区回读</string>
|
||||
<string name="enable_gpu_buffer_readback_description">在上传前回读经由 GPU 修改过的缓冲区数据,以将其保留。一些游戏会用到这项设定以正确渲染某些效果。如果硬件无法处理额外的工作负载,则可能会导致问题。</string>
|
||||
<string name="use_optimized_vertex_buffers">优化顶点缓冲区</string>
|
||||
<string name="use_optimized_vertex_buffers_description">启用经过优化的顶点缓冲区绑定以提升性能。需要 Mesa 26.0 及以上版本的 Turnip 或 QCOM 驱动程序。若使用较旧版本的 Turnip 驱动 (25.3 及以下版本) 则会导致崩溃。</string>
|
||||
|
||||
|
||||
@@ -505,6 +505,8 @@
|
||||
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
|
||||
<string name="enable_buffer_history">Enable buffer history</string>
|
||||
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
|
||||
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
|
||||
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
|
||||
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
|
||||
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
|
||||
|
||||
|
||||
@@ -576,6 +576,13 @@ struct Values {
|
||||
false,
|
||||
#endif
|
||||
"rescale_hack", Category::RendererHacks};
|
||||
SwitchableSetting<bool> enable_gpu_buffer_readback{linkage,
|
||||
false,
|
||||
"enable_gpu_buffer_readback",
|
||||
Category::RendererAdvanced,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> use_asynchronous_shaders{linkage, false, "use_asynchronous_shaders",
|
||||
Category::RendererHacks};
|
||||
|
||||
+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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,6 +226,8 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QObject* parent) {
|
||||
tr("Controls the DMA read mode.\nUnsafe is faster, while Safe is more stable and can fix issues in some games.\nDefault follows the GPU Accuracy setting."));
|
||||
INSERT(Settings, gpu_fence_behavior, tr("GPU Fence Behavior:"),
|
||||
tr("Controls the GPU fence synchronization behavior.\nImmediate is the fastest option, but can introduce some issues.\nBalanced offers better compatibility and may fix issues in some games.\nAccurate further improves compatibility at the cost of some performance.\nStrict is the slowest option, but can fix issues that require stricter synchronization.\nDefault follows the GPU Accuracy setting."));
|
||||
INSERT(Settings, enable_gpu_buffer_readback, tr("Enable GPU buffer readback"),
|
||||
tr("Preserves GPU-modified data by reading it back before uploading.\nSome games require this to render certain effects properly."));
|
||||
INSERT(Settings, use_asynchronous_shaders, tr("Enable asynchronous shader compilation"),
|
||||
tr("May reduce shader stutter."));
|
||||
INSERT(Settings, fast_gpu_time, tr("Fast GPU Time"),
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -1448,8 +1448,8 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size) {
|
||||
const BufferId buffer_id = page_table[page];
|
||||
if (buffer_id) {
|
||||
Buffer& buffer = slot_buffers[buffer_id];
|
||||
WaitForGpuFenceIfNeeded(buffer);
|
||||
if (buffer.IsInBounds(device_addr, size)) {
|
||||
SynchronizeBufferIfNeeded(buffer);
|
||||
return buffer_id;
|
||||
}
|
||||
}
|
||||
@@ -1457,28 +1457,16 @@ BufferId BufferCache<P>::FindBuffer(DAddr device_addr, u32 size) {
|
||||
}
|
||||
|
||||
template <class P>
|
||||
void BufferCache<P>::SynchronizeBufferIfNeeded(Buffer& buffer) {
|
||||
const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate();
|
||||
const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict();
|
||||
const bool should_sync = gpu_fence_accurate || gpu_fence_strict;
|
||||
if (should_sync) {
|
||||
if (gpu_fence_accurate) {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
const bool should_wait = buffer.getWriteTick() > runtime.KnownGpuTick() + 3;
|
||||
if (should_wait) {
|
||||
runtime.Wait(buffer.getWriteTick());
|
||||
}
|
||||
}
|
||||
} else if (gpu_fence_strict) {
|
||||
bool should_download = true;
|
||||
if constexpr (!IS_OPENGL) {
|
||||
should_download = buffer.getWriteTick() > runtime.KnownGpuTick();
|
||||
if (should_download) {
|
||||
runtime.Wait(buffer.getWriteTick());
|
||||
}
|
||||
}
|
||||
if (should_download) {
|
||||
DownloadBufferMemory(buffer);
|
||||
void BufferCache<P>::WaitForGpuFenceIfNeeded(Buffer& buffer) {
|
||||
if constexpr (!IS_OPENGL) {
|
||||
const bool gpu_fence_accurate = Settings::IsGPUFenceBehaviorAccurate();
|
||||
const bool gpu_fence_strict = Settings::IsGPUFenceBehaviorStrict();
|
||||
if (gpu_fence_accurate || gpu_fence_strict) {
|
||||
const u64 gpu_tick_delay = gpu_fence_strict ? 0 : 3;
|
||||
const u64 buffer_tick = buffer.getWriteTick();
|
||||
const u64 gpu_tick = runtime.KnownGpuTick();
|
||||
if (buffer_tick > gpu_tick + gpu_tick_delay) {
|
||||
runtime.Wait(buffer_tick);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1700,6 +1688,9 @@ void BufferCache<P>::ImmediateUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||
if (immediate_buffer.empty()) {
|
||||
immediate_buffer = ImmediateBuffer(largest_copy);
|
||||
}
|
||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||
}
|
||||
device_memory.ReadBlockUnsafe(device_addr, immediate_buffer.data(), copy.size);
|
||||
upload_span = immediate_buffer.subspan(0, copy.size);
|
||||
}
|
||||
@@ -1718,6 +1709,9 @@ void BufferCache<P>::MappedUploadMemory([[maybe_unused]] Buffer& buffer,
|
||||
for (BufferCopy& copy : copies) {
|
||||
u8* const src_pointer = staging_pointer.data() + copy.src_offset;
|
||||
const DAddr device_addr = buffer.CpuAddr() + copy.dst_offset;
|
||||
if (Settings::values.enable_gpu_buffer_readback.GetValue()) {
|
||||
DownloadBufferMemory(buffer, device_addr, copy.size);
|
||||
}
|
||||
device_memory.ReadBlockUnsafe(device_addr, src_pointer, copy.size);
|
||||
// Apply the staging offset
|
||||
copy.src_offset += upload_staging.offset;
|
||||
|
||||
@@ -435,8 +435,6 @@ private:
|
||||
|
||||
bool SynchronizeBuffer(Buffer& buffer, DAddr device_addr, u32 size);
|
||||
|
||||
void SynchronizeBufferIfNeeded(Buffer& buffer);
|
||||
|
||||
void UploadMemory(Buffer& buffer, u64 total_size_bytes, u64 largest_copy,
|
||||
std::span<BufferCopy> copies);
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -252,8 +252,11 @@ void RendererVulkan::RenderScreenshot(std::span<const Tegra::FramebufferConfig>
|
||||
return;
|
||||
}
|
||||
|
||||
const auto screenshot_layers = Tegra::FilterLayerStack(
|
||||
framebuffers, renderer_settings.screenshot_layer_stack, screenshot_layer_scratch);
|
||||
|
||||
const auto& layout{renderer_settings.screenshot_framebuffer_layout};
|
||||
const auto dst_buffer = RenderToBuffer(framebuffers, layout, VK_FORMAT_B8G8R8A8_UNORM,
|
||||
const auto dst_buffer = RenderToBuffer(screenshot_layers, layout, VK_FORMAT_B8G8R8A8_UNORM,
|
||||
layout.width * layout.height * 4);
|
||||
|
||||
std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(),
|
||||
@@ -292,6 +295,12 @@ std::vector<u8> RendererVulkan::GetAppletCaptureBuffer() {
|
||||
|
||||
void RendererVulkan::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;
|
||||
|
||||
if (!applet_frame.image) {
|
||||
applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat);
|
||||
applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat);
|
||||
@@ -300,7 +309,7 @@ void RendererVulkan::RenderAppletCaptureLayer(
|
||||
}
|
||||
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
blit_applet.DrawToFrame(device, rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1,
|
||||
blit_applet.DrawToFrame(device, rasterizer, &applet_frame, capture_layers, VideoCore::Capture::Layout, 1,
|
||||
CaptureFormat);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -72,6 +72,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;
|
||||
|
||||
Tegra::MaxwellDeviceMemoryManager& device_memory;
|
||||
Tegra::GPU& gpu;
|
||||
|
||||
|
||||
@@ -586,6 +586,14 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading
|
||||
if (title_id == 0) {
|
||||
return;
|
||||
}
|
||||
if (!pipeline_cache_filename.empty()) {
|
||||
serialization_thread.WaitForRequests();
|
||||
if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) {
|
||||
SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache,
|
||||
CACHE_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
const auto shader_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir)};
|
||||
const auto base_dir{shader_dir / fmt::format("{:016x}", title_id)};
|
||||
if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) {
|
||||
|
||||
@@ -1966,12 +1966,37 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa
|
||||
render_window->Exit();
|
||||
});
|
||||
|
||||
QtCommon::system->RegisterApplicationChangedCallback([this](u64 changed_program_id) {
|
||||
if (QtCommon::emu_thread)
|
||||
QtCommon::emu_thread->RequestDiskShaderCacheReload(changed_program_id);
|
||||
|
||||
QMetaObject::invokeMethod(
|
||||
this, [this, changed_program_id] { this->OnApplicationChanged(changed_program_id); },
|
||||
Qt::QueuedConnection);
|
||||
});
|
||||
|
||||
connect(render_window, &GRenderWindow::Closed, this, &MainWindow::OnStopGame);
|
||||
connect(render_window, &GRenderWindow::MouseActivity, this, &MainWindow::OnMouseActivity);
|
||||
|
||||
connect(QtCommon::emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
|
||||
&LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
|
||||
|
||||
connect(
|
||||
QtCommon::emu_thread.get(), &EmuThread::ShaderCacheReloadStarted, this,
|
||||
[this] {
|
||||
loading_screen->Prepare(QtCommon::system->GetAppLoader());
|
||||
loading_screen->show();
|
||||
render_window->hide();
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(
|
||||
QtCommon::emu_thread.get(), &EmuThread::ShaderCacheReloadFinished, this,
|
||||
[this] {
|
||||
loading_screen->OnLoadComplete();
|
||||
},
|
||||
Qt::QueuedConnection);
|
||||
|
||||
// Update the GUI
|
||||
UpdateStatusButtons();
|
||||
if (ui->action_Single_Window_Mode->isChecked()) {
|
||||
@@ -3994,6 +4019,41 @@ void MainWindow::OnEmulatorUpdateAvailable() {
|
||||
}
|
||||
#endif
|
||||
|
||||
void MainWindow::OnApplicationChanged(u64 program_id) {
|
||||
if (!emulation_running || program_id == 0)
|
||||
return;
|
||||
|
||||
std::string title_name;
|
||||
std::string title_version;
|
||||
|
||||
const FileSys::PatchManager pm(program_id, QtCommon::system->GetFileSystemController(),
|
||||
QtCommon::system->GetContentProvider());
|
||||
if (const auto metadata = pm.GetControlMetadata(); metadata.first != nullptr) {
|
||||
title_version = metadata.first->GetVersionString();
|
||||
title_name = metadata.first->GetApplicationName();
|
||||
}
|
||||
if (title_name.empty()) {
|
||||
title_name = fmt::format("{:016X}", program_id);
|
||||
}
|
||||
|
||||
if (const auto* process = QtCommon::system->Kernel().ApplicationProcess(); process != nullptr) {
|
||||
const auto instruction_set_suffix = process->Is64Bit() ? tr("(64-bit)") : tr("(32-bit)");
|
||||
title_name =
|
||||
tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit")
|
||||
.arg(QString::fromStdString(title_name), instruction_set_suffix)
|
||||
.toStdString();
|
||||
}
|
||||
|
||||
LOG_INFO(Frontend, "Now running: {:016X} | {} | {}", program_id, title_name, title_version);
|
||||
|
||||
UpdateWindowTitle(title_name, title_version,
|
||||
QtCommon::system->GPU().Renderer().GetDeviceVendor());
|
||||
|
||||
// Switch to record playtime for the running program
|
||||
if (play_time_manager)
|
||||
play_time_manager->SetProgramId(program_id);
|
||||
}
|
||||
|
||||
void MainWindow::UpdateWindowTitle(std::string_view title_name, std::string_view title_version,
|
||||
std::string_view gpu_vendor) {
|
||||
static const std::string build_id = std::string{Common::g_build_id};
|
||||
|
||||
@@ -438,6 +438,7 @@ private:
|
||||
ContentManager::InstallResult InstallNCA(const QString& filename);
|
||||
void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {},
|
||||
std::string_view gpu_vendor = {});
|
||||
void OnApplicationChanged(u64 program_id);
|
||||
void UpdateDockedButton();
|
||||
void UpdateAPIText();
|
||||
void UpdateFilterText();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
@@ -28,6 +29,8 @@
|
||||
#include "input_common/main.h"
|
||||
#include "network/network.h"
|
||||
#include "sdl_config.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "yuzu_cmd/emu_window/emu_window_sdl3.h"
|
||||
#ifdef HAS_OPENGL
|
||||
@@ -462,6 +465,30 @@ int main(int argc, char** argv) {
|
||||
// Just exit right away.
|
||||
exit(0);
|
||||
});
|
||||
|
||||
// QLaunch launched applications (should) now reload shader cache.
|
||||
static std::mutex shader_cache_reload_mutex;
|
||||
system.RegisterApplicationChangedCallback([&system](u64 changed_program_id) {
|
||||
if (!Settings::values.use_disk_shader_cache.GetValue()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{shader_cache_reload_mutex};
|
||||
|
||||
LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", changed_program_id);
|
||||
|
||||
system.Pause();
|
||||
system.GPU().WaitForIdle();
|
||||
system.GPU().ObtainContext();
|
||||
|
||||
system.Renderer().ReadRasterizer()->LoadDiskResources(
|
||||
changed_program_id, std::stop_token{},
|
||||
[](VideoCore::LoadCallbackStage, size_t, size_t) {});
|
||||
|
||||
system.GPU().ReleaseContext();
|
||||
system.Run();
|
||||
});
|
||||
|
||||
void(system.Run());
|
||||
if (system.DebuggerEnabled()) {
|
||||
system.InitializeDebugger();
|
||||
|
||||
Reference in New Issue
Block a user