[hle,display,overlay,starter,hid] add overlay functions, starter applet (initially), HID handheld for system applets and fw21 stubs (#3080)

Adds fully functional overlay display.
- Enable Overlay Applet via "View" -> "Enable Overlay Display Applet"
- Open the overlay by pressing the home button for over 1s
- Can adjust volume
- Can toggle airplane mode (if on WiFi, maybe if overlay is enabled pretend to be on WiFi?)
- Future TODO(?): Adjust Brightness implementation for host system
- Inputs are properly registered. e.g. if overlay open, application does not register inputs.

You can control volume and airplane mode outside of the emulator window

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3080
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: crueter <crueter@eden-emu.dev>
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-committed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
Maufeat 2025-11-27 19:46:41 +01:00 committed by crueter
parent 1efef85352
commit f58097e814
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
75 changed files with 1634 additions and 181 deletions

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@ -131,6 +134,38 @@ Result Container::SetLayerBlending(u64 layer_id, bool enabled) {
R_SUCCEED();
}
Result Container::SetLayerZIndex(u64 layer_id, s32 z_index) {
std::scoped_lock lk{m_lock};
auto* const layer = m_layers.GetLayerById(layer_id);
R_UNLESS(layer != nullptr, VI::ResultNotFound);
if (auto layer_ref = m_surface_flinger->FindLayer(layer->GetConsumerBinderId())) {
LOG_DEBUG(Service_VI, "called, SetLayerZIndex layer_id={} z={} (cid={})", layer_id,
z_index, layer->GetConsumerBinderId());
layer_ref->z_index = z_index;
} else {
LOG_DEBUG(Service_VI, "called, SetLayerZIndex failed to find layer for layer_id={} (cid={})",
layer_id, layer->GetConsumerBinderId());
}
R_SUCCEED();
}
Result Container::GetLayerZIndex(u64 layer_id, s32* out_z_index) {
std::scoped_lock lk{m_lock};
auto* const layer = m_layers.GetLayerById(layer_id);
R_UNLESS(layer != nullptr, VI::ResultNotFound);
if (auto layer_ref = m_surface_flinger->FindLayer(layer->GetConsumerBinderId())) {
*out_z_index = layer_ref->z_index;
R_SUCCEED();
}
R_RETURN(VI::ResultNotFound);
}
void Container::LinkVsyncEvent(u64 display_id, Event* event) {
std::scoped_lock lk{m_lock};
m_conductor->LinkVsyncEvent(display_id, event);

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@ -62,6 +65,8 @@ public:
Result SetLayerVisibility(u64 layer_id, bool visible);
Result SetLayerBlending(u64 layer_id, bool enabled);
Result SetLayerZIndex(u64 layer_id, s32 z_index);
Result GetLayerZIndex(u64 layer_id, s32* out_z_index);
void LinkVsyncEvent(u64 display_id, Event* event);
void UnlinkVsyncEvent(u64 display_id, Event* event);

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@ -116,6 +119,10 @@ Result IManagerDisplayService::SetLayerBlending(bool enabled, u64 layer_id) {
R_RETURN(m_container->SetLayerBlending(layer_id, enabled));
}
Result IManagerDisplayService::SetLayerZIndex(s32 z_index, u64 layer_id) {
R_RETURN(m_container->SetLayerZIndex(layer_id, z_index));
}
Result IManagerDisplayService::CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,
AppletResourceUserId aruid) {
LOG_DEBUG(Service_VI, "called. flags={}, display={}, aruid={}", flags, display_id, aruid.pid);

View file

@ -1,6 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2025 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
#pragma once
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h"
@ -22,6 +27,7 @@ public:
void DestroySharedLayerSession(Kernel::KProcess* owner_process);
Result SetLayerBlending(bool enabled, u64 layer_id);
Result SetLayerZIndex(s32 z_index, u64 layer_id);
public:
Result CreateManagedLayer(Out<u64> out_layer_id, u32 flags, u64 display_id,

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -38,13 +41,13 @@ Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_
Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
Kernel::KMemoryManager::Direction::FromBack)));
// Fill the output data with red.
// Initialize to fully transparent black to avoid covering content before first present.
for (auto& block : *pg) {
u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress());
u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
for (; start < end; start++) {
*start = 0xFF0000FF;
for (; start < end; ++start) {
*start = 0x00000000; // ARGB/RGBA with alpha=0
}
}
@ -252,8 +255,9 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id),
std::addressof(session.layer_id), display_id));
// Configure blending.
// Configure blending and z-index
R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending));
R_ASSERT(m_container.SetLayerZIndex(session.layer_id, 100000));
// Get the producer and set preallocated buffers.
std::shared_ptr<android::BufferQueueProducer> producer;
@ -370,6 +374,11 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
android::Status::NoError,
VI::ResultOperationFailed);
// Ensure the layer is visible when content is presented.
// Re-assert overlay priority in case clients reset it.
(void)m_container.SetLayerZIndex(layer_id, 100000);
(void)m_container.SetLayerVisibility(layer_id, true);
// We succeeded.
R_SUCCEED();
}
@ -406,23 +415,51 @@ Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32*
// 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());
for (; start < end; start++) {
*start = 0;
u8* const block_start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
u8* ptr = block_start;
u8* const block_end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
for (; ptr < block_end; ++ptr) {
if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
*start = capture_buffer[e];
*ptr = capture_buffer[static_cast<size_t>(e)];
} else {
*ptr = 0;
}
e++;
++e;
}
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
m_system.GPU().InvalidateRegion(addr, end - start);
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(block_start, scratch, [&](DAddr addr) {
m_system.GPU().InvalidateRegion(addr, block_end - block_start);
});
}
// After writing, present a frame on each active shared layer so it becomes visible.
for (auto& [aruid, session] : m_sessions) {
std::shared_ptr<android::BufferQueueProducer> producer;
if (R_FAILED(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id))) {
continue;
}
s32 slot = -1;
android::Fence fence = android::Fence::NoFence();
if (producer->DequeueBuffer(&slot, &fence, SharedBufferAsync != 0, SharedBufferWidth,
SharedBufferHeight, SharedBufferBlockLinearFormat, 0) !=
android::Status::NoError) {
continue;
}
std::shared_ptr<android::GraphicBuffer> gb;
if (producer->RequestBuffer(slot, &gb) != android::Status::NoError) {
producer->CancelBuffer(slot, android::Fence::NoFence());
continue;
}
android::QueueBufferInput qin{};
android::QueueBufferOutput qout{};
qin.crop = {0, 0, static_cast<s32>(SharedBufferWidth), static_cast<s32>(SharedBufferHeight)};
qin.fence = android::Fence::NoFence();
qin.transform = static_cast<android::NativeWindowTransform>(0);
qin.swap_interval = 1;
(void)producer->QueueBuffer(slot, qin, &qout);
}
*out_was_written = true;
*out_layer_index = 1;
R_SUCCEED();

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@ -8,7 +11,6 @@
#include "common/math_util.h"
#include "core/hle/service/nvdrv/core/container.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/nvnflinger/ui/fence.h"
namespace Kernel {

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@ -20,7 +23,7 @@ ISystemDisplayService::ISystemDisplayService(Core::System& system_,
{1204, nullptr, "SetDisplayMagnification"},
{2201, nullptr, "SetLayerPosition"},
{2203, nullptr, "SetLayerSize"},
{2204, nullptr, "GetLayerZ"},
{2204, C<&ISystemDisplayService::GetLayerZ>, "GetLayerZ"},
{2205, C<&ISystemDisplayService::SetLayerZ>, "SetLayerZ"},
{2207, C<&ISystemDisplayService::SetLayerVisibility>, "SetLayerVisibility"},
{2209, nullptr, "SetLayerAlpha"},
@ -68,16 +71,26 @@ ISystemDisplayService::ISystemDisplayService(Core::System& system_,
ISystemDisplayService::~ISystemDisplayService() = default;
Result ISystemDisplayService::SetLayerZ(u32 z_value, u64 layer_id) {
LOG_WARNING(Service_VI, "(STUBBED) called. layer_id={}, z_value={}", layer_id, z_value);
Result ISystemDisplayService::GetLayerZ(Out<u64> out_z_value, u64 layer_id) {
LOG_DEBUG(Service_VI, "called. layer_id={}", layer_id);
s32 z{};
const auto res = m_container->GetLayerZIndex(layer_id, &z);
R_TRY(res);
*out_z_value = static_cast<u64>(z);
R_SUCCEED();
}
Result ISystemDisplayService::SetLayerZ(u64 layer_id, u64 z_value) {
LOG_DEBUG(Service_VI, "called. layer_id={}, z_value={}", layer_id, z_value);
// Forward to container using internal API when available
R_RETURN(m_container->SetLayerZIndex(layer_id, static_cast<s32>(z_value)));
}
// This function currently does nothing but return a success error code in
// the vi library itself, so do the same thing, but log out the passed in values.
Result ISystemDisplayService::SetLayerVisibility(bool visible, u64 layer_id) {
LOG_DEBUG(Service_VI, "called, layer_id={}, visible={}", layer_id, visible);
R_SUCCEED();
R_RETURN(m_container->SetLayerVisibility(layer_id, visible));
}
Result ISystemDisplayService::ListDisplayModes(

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 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
@ -18,7 +21,8 @@ public:
~ISystemDisplayService() override;
private:
Result SetLayerZ(u32 z_value, u64 layer_id);
Result GetLayerZ(Out<u64> out_z_value, u64 layer_id);
Result SetLayerZ(u64 layer_id, u64 z_value);
Result SetLayerVisibility(bool visible, u64 layer_id);
Result ListDisplayModes(Out<u64> out_count, u64 display_id,
OutArray<DisplayMode, BufferAttr_HipcMapAlias> out_display_modes);