Browse Source

[core, display, overlay] Add LayerIsOverlay to separate overlay on composer, stub RequestListSummaryOverlayNotification, sync emu settings when setting language (#3123)

This should fix the issue with, for example, ToTK running at 60 FPS when overlay applet is running.
This also should always run the overlay as actual overlay and not in the back.
Stubs RequestListSummaryOverlayNotifications in friends
Syncs Language of the Emulator, when setting language, this is used in Starter Applet

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3123
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
Co-authored-by: Maufeat <sahyno1996@gmail.com>
Co-committed-by: Maufeat <sahyno1996@gmail.com>
pull/3174/head
Maufeat 5 days ago
committed by crueter
parent
commit
1eed7efd09
No known key found for this signature in database GPG Key ID: 425ACD2D4830EBC6
  1. 12
      src/core/hle/service/am/display_layer_manager.cpp
  2. 32
      src/core/hle/service/am/service/overlay_functions.cpp
  3. 1
      src/core/hle/service/am/service/overlay_functions.h
  4. 4
      src/core/hle/service/am/window_system.cpp
  5. 12
      src/core/hle/service/friend/friend.cpp
  6. 51
      src/core/hle/service/ns/application_manager_interface.cpp
  7. 4
      src/core/hle/service/ns/application_manager_interface.h
  8. 5
      src/core/hle/service/ns/ns_types.h
  9. 3
      src/core/hle/service/nvnflinger/display.h
  10. 63
      src/core/hle/service/nvnflinger/hardware_composer.cpp
  11. 4
      src/core/hle/service/nvnflinger/hardware_composer.h
  12. 10
      src/core/hle/service/nvnflinger/surface_flinger.cpp
  13. 1
      src/core/hle/service/nvnflinger/surface_flinger.h
  14. 26
      src/core/hle/service/set/system_settings_server.cpp
  15. 3
      src/core/hle/service/vi/conductor.cpp
  16. 10
      src/core/hle/service/vi/container.cpp
  17. 1
      src/core/hle/service/vi/container.h
  18. 55
      src/core/hle/service/vi/shared_buffer_manager.cpp

12
src/core/hle/service/am/display_layer_manager.cpp

@ -36,10 +36,6 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro
m_buffer_sharing_enabled = false; m_buffer_sharing_enabled = false;
m_blending_enabled = mode == LibraryAppletMode::PartialForeground || m_blending_enabled = mode == LibraryAppletMode::PartialForeground ||
mode == LibraryAppletMode::PartialForegroundIndirectDisplay; mode == LibraryAppletMode::PartialForegroundIndirectDisplay;
if (m_applet_id != AppletId::Application) {
(void)this->IsSystemBufferSharingEnabled();
}
} }
void DisplayLayerManager::Finalize() { void DisplayLayerManager::Finalize() {
@ -80,11 +76,10 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) {
if (m_applet_id != AppletId::Application) { if (m_applet_id != AppletId::Application) {
(void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id); (void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id);
if (m_applet_id == AppletId::OverlayDisplay) { if (m_applet_id == AppletId::OverlayDisplay) {
static constexpr s32 kOverlayBackgroundZ = -1;
(void)m_manager_display_service->SetLayerZIndex(kOverlayBackgroundZ, *out_layer_id);
(void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id);
(void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true);
} else { } else {
static constexpr s32 kOverlayZ = 3;
(void)m_manager_display_service->SetLayerZIndex(kOverlayZ, *out_layer_id);
(void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id);
} }
} }
@ -131,6 +126,7 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() {
s32 initial_z = 1; s32 initial_z = 1;
if (m_applet_id == AppletId::OverlayDisplay) { if (m_applet_id == AppletId::OverlayDisplay) {
initial_z = -1; initial_z = -1;
(void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true);
} }
m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id); m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id);
R_SUCCEED(); R_SUCCEED();

32
src/core/hle/service/am/service/overlay_functions.cpp

@ -24,7 +24,7 @@ namespace Service::AM {
{20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"}, {20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"},
{21, nullptr, "SetHandlingTouchScreenInputEnabled"}, {21, nullptr, "SetHandlingTouchScreenInputEnabled"},
{30, nullptr, "SetHealthWarningShowingState"}, {30, nullptr, "SetHealthWarningShowingState"},
{31, nullptr, "IsHealthWarningRequired"},
{31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"},
{40, nullptr, "GetApplicationNintendoLogo"}, {40, nullptr, "GetApplicationNintendoLogo"},
{41, nullptr, "GetApplicationStartupMovie"}, {41, nullptr, "GetApplicationStartupMovie"},
{50, nullptr, "SetGpuTimeSliceBoostForApplication"}, {50, nullptr, "SetGpuTimeSliceBoostForApplication"},
@ -69,12 +69,33 @@ namespace Service::AM {
Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) { Result IOverlayFunctions::GetApplicationIdForLogo(Out<u64> out_application_id) {
LOG_DEBUG(Service_AM, "called"); LOG_DEBUG(Service_AM, "called");
// Prefer explicit application_id if available, else fall back to program_id
std::shared_ptr<Applet> target_applet;
auto* window_system = system.GetAppletManager().GetWindowSystem();
if (window_system) {
target_applet = window_system->GetMainApplet();
if (target_applet) {
std::scoped_lock lk{target_applet->lock};
LOG_DEBUG(Service_AM, "applet_id={}, program_id={:016X}, type={}",
static_cast<u32>(target_applet->applet_id), target_applet->program_id,
static_cast<u32>(target_applet->type));
u64 id = target_applet->screen_shot_identity.application_id;
if (id == 0) {
id = target_applet->program_id;
}
LOG_DEBUG(Service_AM, "application_id={:016X}", id);
*out_application_id = id;
R_SUCCEED();
}
}
std::scoped_lock lk{m_applet->lock}; std::scoped_lock lk{m_applet->lock};
u64 id = m_applet->screen_shot_identity.application_id; u64 id = m_applet->screen_shot_identity.application_id;
if (id == 0) { if (id == 0) {
id = m_applet->program_id; id = m_applet->program_id;
} }
LOG_DEBUG(Service_AM, "application_id={:016X} (fallback)", id);
*out_application_id = id; *out_application_id = id;
R_SUCCEED(); R_SUCCEED();
} }
@ -86,6 +107,13 @@ namespace Service::AM {
R_SUCCEED(); R_SUCCEED();
} }
Result IOverlayFunctions::IsHealthWarningRequired(Out<bool> is_required) {
LOG_DEBUG(Service_AM, "called");
std::scoped_lock lk{m_applet->lock};
*is_required = false;
R_SUCCEED();
}
Result IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled(bool enabled) { Result IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled(bool enabled) {
LOG_DEBUG(Service_AM, "called, enabled={}", enabled); LOG_DEBUG(Service_AM, "called, enabled={}", enabled);
std::scoped_lock lk{m_applet->lock}; std::scoped_lock lk{m_applet->lock};

1
src/core/hle/service/am/service/overlay_functions.h

@ -18,6 +18,7 @@ namespace Service::AM {
Result EndToWatchShortHomeButtonMessage(); Result EndToWatchShortHomeButtonMessage();
Result GetApplicationIdForLogo(Out<u64> out_application_id); Result GetApplicationIdForLogo(Out<u64> out_application_id);
Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled); Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled);
Result IsHealthWarningRequired(Out<bool> is_required);
Result SetHandlingHomeButtonShortPressedEnabled(bool enabled); Result SetHandlingHomeButtonShortPressedEnabled(bool enabled);
Result Unknown70(); Result Unknown70();

4
src/core/hle/service/am/window_system.cpp

@ -186,8 +186,6 @@ void WindowSystem::OnSystemButtonPress(SystemButtonType type) {
if (m_overlay_display) { if (m_overlay_display) {
std::scoped_lock lk_overlay{m_overlay_display->lock}; std::scoped_lock lk_overlay{m_overlay_display->lock};
m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground; m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground;
// Tie window visibility to foreground state so hidden when not active
m_overlay_display->window_visible = 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); LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible);
} }
SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton); SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton);
@ -393,7 +391,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
s32 z_index = 0; s32 z_index = 0;
const bool now_foreground = inherited_foreground; const bool now_foreground = inherited_foreground;
if (applet->applet_id == AppletId::OverlayDisplay) { if (applet->applet_id == AppletId::OverlayDisplay) {
z_index = applet->overlay_in_foreground ? 100000 : -100000;
z_index = applet->overlay_in_foreground ? 100000 : -1;
} else if (now_foreground && !is_obscured) { } else if (now_foreground && !is_obscured) {
z_index = 2; z_index = 2;
} else if (now_foreground) { } else if (now_foreground) {

12
src/core/hle/service/friend/friend.cpp

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -67,7 +70,7 @@ public:
{20701, &IFriendService::GetPlayHistoryStatistics, "GetPlayHistoryStatistics"}, {20701, &IFriendService::GetPlayHistoryStatistics, "GetPlayHistoryStatistics"},
{20800, &IFriendService::LoadUserSetting, "LoadUserSetting"}, {20800, &IFriendService::LoadUserSetting, "LoadUserSetting"},
{20801, nullptr, "SyncUserSetting"}, {20801, nullptr, "SyncUserSetting"},
{20900, nullptr, "RequestListSummaryOverlayNotification"},
{20900, &IFriendService::RequestListSummaryOverlayNotification, "RequestListSummaryOverlayNotification"},
{21000, nullptr, "GetExternalApplicationCatalog"}, {21000, nullptr, "GetExternalApplicationCatalog"},
{22000, nullptr, "GetReceivedFriendInvitationList"}, {22000, nullptr, "GetReceivedFriendInvitationList"},
{22001, nullptr, "GetReceivedFriendInvitationDetailedInfo"}, {22001, nullptr, "GetReceivedFriendInvitationDetailedInfo"},
@ -317,6 +320,13 @@ private:
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
void RequestListSummaryOverlayNotification(HLERequestContext& ctx) {
LOG_INFO(Service_Friend, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void GetReceivedFriendInvitationCountCache(HLERequestContext& ctx) { void GetReceivedFriendInvitationCountCache(HLERequestContext& ctx) {
LOG_DEBUG(Service_Friend, "(STUBBED) called, check in out"); LOG_DEBUG(Service_Friend, "(STUBBED) called, check in out");

51
src/core/hle/service/ns/application_manager_interface.cpp

@ -10,6 +10,8 @@
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/application_manager_interface.h" #include "core/hle/service/ns/application_manager_interface.h"
#include "core/file_sys/content_archive.h"
#include "core/hle/service/ns/content_management_interface.h" #include "core/hle/service/ns/content_management_interface.h"
#include "core/hle/service/ns/read_only_application_control_data_interface.h" #include "core/hle/service/ns/read_only_application_control_data_interface.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
@ -54,7 +56,7 @@ IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_
{37, nullptr, "ListRequiredVersion"}, {37, nullptr, "ListRequiredVersion"},
{38, D<&IApplicationManagerInterface::CheckApplicationLaunchVersion>, "CheckApplicationLaunchVersion"}, {38, D<&IApplicationManagerInterface::CheckApplicationLaunchVersion>, "CheckApplicationLaunchVersion"},
{39, nullptr, "CheckApplicationLaunchRights"}, {39, nullptr, "CheckApplicationLaunchRights"},
{40, nullptr, "GetApplicationLogoData"},
{40, D<&IApplicationManagerInterface::GetApplicationLogoData>, "GetApplicationLogoData"},
{41, nullptr, "CalculateApplicationDownloadRequiredSize"}, {41, nullptr, "CalculateApplicationDownloadRequiredSize"},
{42, nullptr, "CleanupSdCard"}, {42, nullptr, "CleanupSdCard"},
{43, D<&IApplicationManagerInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"}, {43, D<&IApplicationManagerInterface::CheckSdCardMountStatus>, "CheckSdCardMountStatus"},
@ -331,6 +333,53 @@ Result IApplicationManagerInterface::UnregisterNetworkServiceAccountWithUserSave
LOG_DEBUG(Service_NS, "called, user_id={}", user_id.FormattedString()); LOG_DEBUG(Service_NS, "called, user_id={}", user_id.FormattedString());
R_SUCCEED(); R_SUCCEED();
} }
Result IApplicationManagerInterface::GetApplicationLogoData(
Out<s64> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, u64 application_id,
InBuffer<BufferAttr_HipcMapAlias> logo_path_buffer) {
const std::string path_view{reinterpret_cast<const char*>(logo_path_buffer.data()),
logo_path_buffer.size()};
// Find null terminator and trim the path
auto null_pos = path_view.find('\0');
std::string path = (null_pos != std::string::npos) ? path_view.substr(0, null_pos) : path_view;
LOG_DEBUG(Service_NS, "called, application_id={:016X}, logo_path={}", application_id, path);
auto& content_provider = system.GetContentProviderUnion();
auto program = content_provider.GetEntry(application_id, FileSys::ContentRecordType::Program);
if (!program) {
LOG_WARNING(Service_NS, "Application program not found for id={:016X}", application_id);
R_RETURN(ResultUnknown);
}
const auto logo_dir = program->GetLogoPartition();
if (!logo_dir) {
LOG_WARNING(Service_NS, "Logo partition not found for id={:016X}", application_id);
R_RETURN(ResultUnknown);
}
const auto file = logo_dir->GetFile(path);
if (!file) {
LOG_WARNING(Service_NS, "Logo path not found: {} for id={:016X}", path,
application_id);
R_RETURN(ResultUnknown);
}
const auto data = file->ReadAllBytes();
if (data.size() > out_buffer.size()) {
LOG_WARNING(Service_NS, "Logo buffer too small: have={}, need={}", out_buffer.size(),
data.size());
R_RETURN(ResultUnknown);
}
std::memcpy(out_buffer.data(), data.data(), data.size());
*out_size = static_cast<s64>(data.size());
R_SUCCEED();
}
Result IApplicationManagerInterface::GetApplicationControlData( Result IApplicationManagerInterface::GetApplicationControlData(
OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size, OutBuffer<BufferAttr_HipcMapAlias> out_buffer, Out<u32> out_actual_size,
ApplicationControlSource application_control_source, u64 application_id) { ApplicationControlSource application_control_source, u64 application_id) {

4
src/core/hle/service/ns/application_manager_interface.h

@ -60,6 +60,10 @@ public:
u64 application_id); u64 application_id);
Result CheckApplicationLaunchVersion(u64 application_id); Result CheckApplicationLaunchVersion(u64 application_id);
Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id); Result GetApplicationTerminateResult(Out<Result> out_result, u64 application_id);
Result GetApplicationLogoData(Out<s64> out_size,
OutBuffer<BufferAttr_HipcMapAlias> out_buffer,
u64 application_id,
InBuffer<BufferAttr_HipcMapAlias> logo_path_buffer);
Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event); Result Unknown4022(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result Unknown4023(Out<u64> out_result); Result Unknown4023(Out<u64> out_result);
Result Unknown4053(); Result Unknown4053();

5
src/core/hle/service/ns/ns_types.h

@ -179,4 +179,9 @@ struct ApplicationDisplayData {
}; };
static_assert(sizeof(ApplicationDisplayData) == 0x300, "ApplicationDisplayData has incorrect size."); static_assert(sizeof(ApplicationDisplayData) == 0x300, "ApplicationDisplayData has incorrect size.");
struct LogoPath {
std::array<char, 0x300> path;
};
static_assert(std::is_trivially_copyable_v<LogoPath>, "LogoPath must be trivially copyable.");
} // namespace Service::NS } // namespace Service::NS

3
src/core/hle/service/nvnflinger/display.h

@ -15,7 +15,7 @@ struct Layer {
explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_, explicit Layer(std::shared_ptr<android::BufferItemConsumer> buffer_item_consumer_,
s32 consumer_id_) s32 consumer_id_)
: buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_), : buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_),
blending(LayerBlending::None), visible(true), z_index(0) {}
blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false) {}
~Layer() { ~Layer() {
buffer_item_consumer->Abandon(); buffer_item_consumer->Abandon();
} }
@ -25,6 +25,7 @@ struct Layer {
LayerBlending blending; LayerBlending blending;
bool visible; bool visible;
s32 z_index; s32 z_index;
bool is_overlay;
}; };
struct LayerStack { struct LayerStack {

63
src/core/hle/service/nvnflinger/hardware_composer.cpp

@ -78,8 +78,25 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
for (auto& layer : display.stack.layers) { for (auto& layer : display.stack.layers) {
auto consumer_id = layer->consumer_id; auto consumer_id = layer->consumer_id;
bool should_try_acquire = true;
if (!layer->is_overlay) {
auto fb_it = m_framebuffers.find(consumer_id);
if (fb_it != m_framebuffers.end() && fb_it->second.is_acquired) {
const u64 frames_since_last_acquire = m_frame_number - fb_it->second.last_acquire_frame;
const s32 expected_interval = NormalizeSwapInterval(nullptr, fb_it->second.item.swap_interval);
if (frames_since_last_acquire < static_cast<u64>(expected_interval)) {
should_try_acquire = false;
}
}
}
// Try to fetch the framebuffer (either new or stale). // Try to fetch the framebuffer (either new or stale).
const auto result = this->CacheFramebufferLocked(*layer, consumer_id);
const auto result = should_try_acquire
? this->CacheFramebufferLocked(*layer, consumer_id)
: (m_framebuffers.find(consumer_id) != m_framebuffers.end() && m_framebuffers[consumer_id].is_acquired
? CacheStatus::CachedBufferReused
: CacheStatus::NoBufferAvailable);
// If we failed, skip this layer. // If we failed, skip this layer.
if (result == CacheStatus::NoBufferAvailable) { if (result == CacheStatus::NoBufferAvailable) {
@ -111,6 +128,12 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
}); });
} }
// Overlay layers run at their own framerate independently of the game.
// Skip them when calculating the swap interval for the main game.
if (layer->is_overlay) {
continue;
}
// We need to compose again either before this frame is supposed to // We need to compose again either before this frame is supposed to
// be released, or exactly on the vsync period it should be released. // be released, or exactly on the vsync period it should be released.
const s32 item_swap_interval = NormalizeSwapInterval(out_speed_scale, item.swap_interval); const s32 item_swap_interval = NormalizeSwapInterval(out_speed_scale, item.swap_interval);
@ -138,33 +161,44 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
// Batch framebuffer releases, instead of one-into-one. // Batch framebuffer releases, instead of one-into-one.
std::vector<std::pair<Layer*, Framebuffer*>> to_release; std::vector<std::pair<Layer*, Framebuffer*>> to_release;
for (auto& [layer_id, framebuffer] : m_framebuffers) { for (auto& [layer_id, framebuffer] : m_framebuffers) {
if (framebuffer.release_frame_number > m_frame_number || !framebuffer.is_acquired)
if (!framebuffer.is_acquired)
continue;
auto layer = display.stack.FindLayer(layer_id);
if (!layer)
continue; continue;
if (auto layer = display.stack.FindLayer(layer_id); layer)
// Overlay layers always release after every compose
// Non-overlay layers release based on their swap interval
if (layer->is_overlay || framebuffer.release_frame_number <= m_frame_number) {
to_release.emplace_back(layer.get(), &framebuffer); to_release.emplace_back(layer.get(), &framebuffer);
}
} }
for (auto& [layer, framebuffer] : to_release) { for (auto& [layer, framebuffer] : to_release) {
layer->buffer_item_consumer->ReleaseBuffer(framebuffer->item, android::Fence::NoFence()); layer->buffer_item_consumer->ReleaseBuffer(framebuffer->item, android::Fence::NoFence());
framebuffer->is_acquired = false; framebuffer->is_acquired = false;
} }
// Advance by at least one frame.
const u32 frame_advance = swap_interval.value_or(1);
m_frame_number += frame_advance;
// Advance by 1 frame (60 FPS compositing)
m_frame_number += 1;
// Release any necessary framebuffers.
// Release any necessary framebuffers (non-overlay layers only, as overlays are already released above).
for (auto& [layer_id, framebuffer] : m_framebuffers) { for (auto& [layer_id, framebuffer] : m_framebuffers) {
if (framebuffer.release_frame_number > m_frame_number) {
// Not yet ready to release this framebuffer.
if (!framebuffer.is_acquired) {
// Already released.
continue; continue;
} }
if (!framebuffer.is_acquired) {
// Already released.
if (framebuffer.release_frame_number > m_frame_number) {
continue; continue;
} }
if (const auto layer = display.stack.FindLayer(layer_id); layer != nullptr) { if (const auto layer = display.stack.FindLayer(layer_id); layer != nullptr) {
// Skip overlay layers as they were already released above
if (layer->is_overlay) {
continue;
}
// TODO: support release fence // TODO: support release fence
// This is needed to prevent screen tearing // This is needed to prevent screen tearing
layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); layer->buffer_item_consumer->ReleaseBuffer(framebuffer.item, android::Fence::NoFence());
@ -172,7 +206,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display,
} }
} }
return frame_advance;
return 1;
} }
void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_id) { void HardwareComposer::RemoveLayerLocked(Display& display, ConsumerId consumer_id) {
@ -200,8 +234,9 @@ bool HardwareComposer::TryAcquireFramebufferLocked(Layer& layer, Framebuffer& fr
} }
// We succeeded, so set the new release frame info. // We succeeded, so set the new release frame info.
framebuffer.release_frame_number =
NormalizeSwapInterval(nullptr, framebuffer.item.swap_interval);
const s32 swap_interval = layer.is_overlay ? 1 : NormalizeSwapInterval(nullptr, framebuffer.item.swap_interval);
framebuffer.release_frame_number = m_frame_number + swap_interval;
framebuffer.last_acquire_frame = m_frame_number;
framebuffer.is_acquired = true; framebuffer.is_acquired = true;
return true; return true;

4
src/core/hle/service/nvnflinger/hardware_composer.h

@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@ -34,6 +37,7 @@ private:
struct Framebuffer { struct Framebuffer {
android::BufferItem item{}; android::BufferItem item{};
ReleaseFrameNumber release_frame_number{}; ReleaseFrameNumber release_frame_number{};
u64 last_acquire_frame{0};
bool is_acquired{false}; bool is_acquired{false};
}; };

10
src/core/hle/service/nvnflinger/surface_flinger.cpp

@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -101,6 +104,13 @@ void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blen
} }
} }
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;
LOG_DEBUG(Service_VI, "Layer {} marked as overlay: {}", consumer_binder_id, is_overlay);
}
}
Display* SurfaceFlinger::FindDisplay(u64 display_id) { Display* SurfaceFlinger::FindDisplay(u64 display_id) {
for (auto& display : m_displays) { for (auto& display : m_displays) {
if (display.id == display_id) { if (display.id == display_id) {

1
src/core/hle/service/nvnflinger/surface_flinger.h

@ -47,6 +47,7 @@ public:
void SetLayerVisibility(s32 consumer_binder_id, bool visible); void SetLayerVisibility(s32 consumer_binder_id, bool visible);
void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending); void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending);
void SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay);
std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id); std::shared_ptr<Layer> FindLayer(s32 consumer_binder_id);

26
src/core/hle/service/set/system_settings_server.cpp

@ -36,6 +36,29 @@ struct SettingsHeader {
u32 version; u32 version;
u32 reserved; u32 reserved;
}; };
void SyncGlobalLanguageFromCode(LanguageCode language_code) {
const auto it = std::find_if(available_language_codes.begin(), available_language_codes.end(),
[language_code](LanguageCode code) { return code == language_code; });
if (it == available_language_codes.end()) {
return;
}
const std::size_t index = static_cast<std::size_t>(std::distance(available_language_codes.begin(), it));
if (index >= static_cast<std::size_t>(Settings::values.language_index.GetValue())) {
Settings::values.language_index.SetValue(static_cast<Settings::Language>(index));
}
}
void SyncGlobalRegionFromCode(SystemRegionCode region_code) {
const auto region_index = static_cast<std::size_t>(region_code);
if (region_index > static_cast<std::size_t>(Settings::Region::Taiwan)) {
return;
}
Settings::values.region_index.SetValue(static_cast<Settings::Region>(region_index));
}
} // Anonymous namespace } // Anonymous namespace
Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system,
@ -457,6 +480,7 @@ Result ISystemSettingsServer::SetLanguageCode(LanguageCode language_code) {
LOG_INFO(Service_SET, "called, language_code={}", language_code); LOG_INFO(Service_SET, "called, language_code={}", language_code);
m_system_settings.language_code = language_code; m_system_settings.language_code = language_code;
SyncGlobalLanguageFromCode(language_code);
SetSaveNeeded(); SetSaveNeeded();
R_SUCCEED(); R_SUCCEED();
} }
@ -889,6 +913,7 @@ Result ISystemSettingsServer::SetRegionCode(SystemRegionCode region_code) {
LOG_INFO(Service_SET, "called, region_code={}", region_code); LOG_INFO(Service_SET, "called, region_code={}", region_code);
m_system_settings.region_code = region_code; m_system_settings.region_code = region_code;
SyncGlobalRegionFromCode(region_code);
SetSaveNeeded(); SetSaveNeeded();
R_SUCCEED(); R_SUCCEED();
} }
@ -1224,6 +1249,7 @@ Result ISystemSettingsServer::SetKeyboardLayout(KeyboardLayout keyboard_layout)
LOG_INFO(Service_SET, "called, keyboard_layout={}", keyboard_layout); LOG_INFO(Service_SET, "called, keyboard_layout={}", keyboard_layout);
m_system_settings.keyboard_layout = keyboard_layout; m_system_settings.keyboard_layout = keyboard_layout;
SetSaveNeeded();
R_SUCCEED(); R_SUCCEED();
} }

3
src/core/hle/service/vi/conductor.cpp

@ -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-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

10
src/core/hle/service/vi/container.cpp

@ -166,6 +166,16 @@ Result Container::GetLayerZIndex(u64 layer_id, s32* out_z_index) {
R_RETURN(VI::ResultNotFound); R_RETURN(VI::ResultNotFound);
} }
Result Container::SetLayerIsOverlay(u64 layer_id, bool is_overlay) {
std::scoped_lock lk{m_lock};
auto* const layer = m_layers.GetLayerById(layer_id);
R_UNLESS(layer != nullptr, VI::ResultNotFound);
m_surface_flinger->SetLayerIsOverlay(layer->GetConsumerBinderId(), is_overlay);
R_SUCCEED();
}
void Container::LinkVsyncEvent(u64 display_id, Event* event) { void Container::LinkVsyncEvent(u64 display_id, Event* event) {
std::scoped_lock lk{m_lock}; std::scoped_lock lk{m_lock};
m_conductor->LinkVsyncEvent(display_id, event); m_conductor->LinkVsyncEvent(display_id, event);

1
src/core/hle/service/vi/container.h

@ -67,6 +67,7 @@ public:
Result SetLayerBlending(u64 layer_id, bool enabled); Result SetLayerBlending(u64 layer_id, bool enabled);
Result SetLayerZIndex(u64 layer_id, s32 z_index); Result SetLayerZIndex(u64 layer_id, s32 z_index);
Result GetLayerZIndex(u64 layer_id, s32* out_z_index); Result GetLayerZIndex(u64 layer_id, s32* out_z_index);
Result SetLayerIsOverlay(u64 layer_id, bool is_overlay);
void LinkVsyncEvent(u64 display_id, Event* event); void LinkVsyncEvent(u64 display_id, Event* event);
void UnlinkVsyncEvent(u64 display_id, Event* event); void UnlinkVsyncEvent(u64 display_id, Event* event);

55
src/core/hle/service/vi/shared_buffer_manager.cpp

@ -46,8 +46,8 @@ Result AllocateSharedBufferMemory(std::unique_ptr<Kernel::KPageGroup>* out_page_
u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress()); u32* start = system.DeviceMemory().GetPointer<u32>(block.GetAddress());
u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize()); u32* end = system.DeviceMemory().GetPointer<u32>(block.GetAddress() + block.GetSize());
for (; start < end; ++start) {
*start = 0x00000000; // ARGB/RGBA with alpha=0
for (; start < end; start++) {
*start = 0xFF0000FF;
} }
} }
@ -257,7 +257,6 @@ Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64*
// Configure blending and z-index // Configure blending and z-index
R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending)); 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. // Get the producer and set preallocated buffers.
std::shared_ptr<android::BufferQueueProducer> producer; std::shared_ptr<android::BufferQueueProducer> producer;
@ -374,11 +373,6 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence,
android::Status::NoError, android::Status::NoError,
VI::ResultOperationFailed); 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. // We succeeded.
R_SUCCEED(); R_SUCCEED();
} }
@ -415,51 +409,22 @@ Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32*
// TODO: this could be optimized // TODO: this could be optimized
s64 e = -1280 * 768 * 4; s64 e = -1280 * 768 * 4;
for (auto& block : *m_buffer_page_group) { for (auto& block : *m_buffer_page_group) {
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());
u8* start = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress());
u8* end = m_system.DeviceMemory().GetPointer<u8>(block.GetAddress() + block.GetSize());
for (; ptr < block_end; ++ptr) {
for (; start < end; start++) {
*start = 0;
if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) { if (e >= 0 && e < static_cast<s64>(capture_buffer.size())) {
*ptr = capture_buffer[static_cast<size_t>(e)];
} else {
*ptr = 0;
*start = capture_buffer[e];
} }
++e;
e++;
} }
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(block_start, scratch, [&](DAddr addr) {
m_system.GPU().InvalidateRegion(addr, block_end - block_start);
m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) {
m_system.GPU().InvalidateRegion(addr, end - 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_was_written = true;
*out_layer_index = 1; *out_layer_index = 1;
R_SUCCEED(); R_SUCCEED();

Loading…
Cancel
Save