diff --git a/src/core/hle/service/am/display_layer_manager.cpp b/src/core/hle/service/am/display_layer_manager.cpp index a93ca5bc67..78832bf97d 100644 --- a/src/core/hle/service/am/display_layer_manager.cpp +++ b/src/core/hle/service/am/display_layer_manager.cpp @@ -80,11 +80,10 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) { if (m_applet_id != AppletId::Application) { (void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id); 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(100000, *out_layer_id); + (void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true); } 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); } } @@ -130,7 +129,8 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() { m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id); s32 initial_z = 1; if (m_applet_id == AppletId::OverlayDisplay) { - initial_z = -1; + initial_z = 100000; + (void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true); } m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id); R_SUCCEED(); diff --git a/src/core/hle/service/am/window_system.cpp b/src/core/hle/service/am/window_system.cpp index c78a955f68..438737c70d 100644 --- a/src/core/hle/service/am/window_system.cpp +++ b/src/core/hle/service/am/window_system.cpp @@ -186,8 +186,6 @@ void WindowSystem::OnSystemButtonPress(SystemButtonType type) { 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; - // 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); } SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton); @@ -393,7 +391,7 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b s32 z_index = 0; const bool now_foreground = inherited_foreground; if (applet->applet_id == AppletId::OverlayDisplay) { - z_index = applet->overlay_in_foreground ? 100000 : -100000; + z_index = 100000; } else if (now_foreground && !is_obscured) { z_index = 2; } else if (now_foreground) { diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp index 84c73e9d6f..cb809777f5 100644 --- a/src/core/hle/service/friend/friend.cpp +++ b/src/core/hle/service/friend/friend.cpp @@ -67,7 +67,7 @@ public: {20701, &IFriendService::GetPlayHistoryStatistics, "GetPlayHistoryStatistics"}, {20800, &IFriendService::LoadUserSetting, "LoadUserSetting"}, {20801, nullptr, "SyncUserSetting"}, - {20900, nullptr, "RequestListSummaryOverlayNotification"}, + {20900, &IFriendService::RequestListSummaryOverlayNotification, "RequestListSummaryOverlayNotification"}, {21000, nullptr, "GetExternalApplicationCatalog"}, {22000, nullptr, "GetReceivedFriendInvitationList"}, {22001, nullptr, "GetReceivedFriendInvitationDetailedInfo"}, @@ -317,6 +317,13 @@ private: 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) { LOG_DEBUG(Service_Friend, "(STUBBED) called, check in out"); diff --git a/src/core/hle/service/nvnflinger/display.h b/src/core/hle/service/nvnflinger/display.h index 1264465079..ada302a79e 100644 --- a/src/core/hle/service/nvnflinger/display.h +++ b/src/core/hle/service/nvnflinger/display.h @@ -15,7 +15,7 @@ struct Layer { explicit Layer(std::shared_ptr 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) {} + blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false) {} ~Layer() { buffer_item_consumer->Abandon(); } @@ -25,6 +25,7 @@ struct Layer { LayerBlending blending; bool visible; s32 z_index; + bool is_overlay; }; struct LayerStack { diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp index 4f8f5da69a..cfd999497b 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/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) { 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(expected_interval)) { + should_try_acquire = false; + } + } + } + // 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 (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 // be released, or exactly on the vsync period it should be released. 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. std::vector> to_release; 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; - 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); + } } for (auto& [layer, framebuffer] : to_release) { layer->buffer_item_consumer->ReleaseBuffer(framebuffer->item, android::Fence::NoFence()); 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) { - if (framebuffer.release_frame_number > m_frame_number) { - // Not yet ready to release this framebuffer. + if (!framebuffer.is_acquired) { + // Already released. continue; } - if (!framebuffer.is_acquired) { - // Already released. + if (framebuffer.release_frame_number > m_frame_number) { continue; } 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 // This is needed to prevent screen tearing 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) { @@ -200,8 +234,9 @@ bool HardwareComposer::TryAcquireFramebufferLocked(Layer& layer, Framebuffer& fr } // 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; return true; diff --git a/src/core/hle/service/nvnflinger/hardware_composer.h b/src/core/hle/service/nvnflinger/hardware_composer.h index c5b8304681..e9b7194612 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.h +++ b/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-License-Identifier: GPL-3.0-or-later @@ -34,6 +37,7 @@ private: struct Framebuffer { android::BufferItem item{}; ReleaseFrameNumber release_frame_number{}; + u64 last_acquire_frame{0}; bool is_acquired{false}; }; diff --git a/src/core/hle/service/nvnflinger/surface_flinger.cpp b/src/core/hle/service/nvnflinger/surface_flinger.cpp index 8362b65e58..fbf70592bd 100644 --- a/src/core/hle/service/nvnflinger/surface_flinger.cpp +++ b/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-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) { for (auto& display : m_displays) { if (display.id == display_id) { diff --git a/src/core/hle/service/nvnflinger/surface_flinger.h b/src/core/hle/service/nvnflinger/surface_flinger.h index d771909a82..05b4206c34 100644 --- a/src/core/hle/service/nvnflinger/surface_flinger.h +++ b/src/core/hle/service/nvnflinger/surface_flinger.h @@ -47,6 +47,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); std::shared_ptr FindLayer(s32 consumer_binder_id); diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp index d69d2252f5..c0b5acd654 100644 --- a/src/core/hle/service/set/system_settings_server.cpp +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -29,13 +29,45 @@ namespace Service::Set { namespace { -constexpr u32 SETTINGS_VERSION{4u}; -constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't'); -struct SettingsHeader { - u64 magic; - u32 version; - u32 reserved; -}; + constexpr u32 SETTINGS_VERSION{4u}; + constexpr auto SETTINGS_MAGIC = Common::MakeMagic('y', 'u', 'z', 'u', '_', 's', 'e', 't'); + struct SettingsHeader { + u64 magic; + u32 version; + u32 reserved; + }; + + void SyncGlobalLanguageFromCode(LanguageCode language_code) { + const auto it = std::find(available_language_codes.begin(), available_language_codes.end(), language_code); + if (it != available_language_codes.end()) { + const auto idx = static_cast(std::distance(available_language_codes.begin(), it)); + ::Settings::values.language_index = static_cast<::Settings::Language>(idx); + } else { + LOG_WARNING(Service_SET, + "Language code {} not found; keeping Settings::values.language_index", + language_code); + } + } + + void SyncGlobalRegionFromCode(SystemRegionCode region_code) { + Settings::Region mapped_region = Settings::Region::Usa; + switch (region_code) { + case SystemRegionCode::Japan: + mapped_region = Settings::Region::Japan; break; + case SystemRegionCode::Usa: + mapped_region = Settings::Region::Usa; break; + case SystemRegionCode::Europe: + mapped_region = Settings::Region::Europe; break; + case SystemRegionCode::Australia: + mapped_region = Settings::Region::Australia; break; + case SystemRegionCode::HongKongTaiwanKorea: + mapped_region = Settings::Region::Korea; break; + case SystemRegionCode::China: + mapped_region = Settings::Region::China; + break; + } + Settings::values.region_index = mapped_region; + } } // Anonymous namespace Result GetFirmwareVersionImpl(FirmwareVersionFormat& out_firmware, Core::System& system, @@ -461,6 +493,7 @@ Result ISystemSettingsServer::SetLanguageCode(LanguageCode language_code) { LOG_INFO(Service_SET, "called, language_code={}", language_code); m_system_settings.language_code = language_code; + SyncGlobalLanguageFromCode(language_code); SetSaveNeeded(); R_SUCCEED(); } @@ -893,6 +926,7 @@ Result ISystemSettingsServer::SetRegionCode(SystemRegionCode region_code) { LOG_INFO(Service_SET, "called, region_code={}", region_code); m_system_settings.region_code = region_code; + SyncGlobalRegionFromCode(region_code); SetSaveNeeded(); R_SUCCEED(); } @@ -1079,9 +1113,10 @@ Result ISystemSettingsServer::SetDeviceNickName( } Result ISystemSettingsServer::GetProductModel(Out out_product_model) { - // Most certainly should be 1 -- definitely should not be 2, but it's worth tinkering with anyways - u32 const product_model = 1; + const u32 product_model = 1; + LOG_WARNING(Service_SET, "(STUBBED) called, product_model={}", product_model); + *out_product_model = product_model; R_SUCCEED(); } @@ -1228,6 +1263,7 @@ Result ISystemSettingsServer::SetKeyboardLayout(KeyboardLayout keyboard_layout) LOG_INFO(Service_SET, "called, keyboard_layout={}", keyboard_layout); m_system_settings.keyboard_layout = keyboard_layout; + SetSaveNeeded(); R_SUCCEED(); } @@ -1350,44 +1386,52 @@ Result ISystemSettingsServer::GetHttpAuthConfigs(Out out_count, OutBuffer(m_swap_interval); + const s32 vsync_interval = 1; + const f32 effective_fps = 60.f / static_cast(vsync_interval); return static_cast(speed_scale * (1000000000.f / effective_fps)); } diff --git a/src/core/hle/service/vi/container.cpp b/src/core/hle/service/vi/container.cpp index e7d3b4757b..e7255c2b0a 100644 --- a/src/core/hle/service/vi/container.cpp +++ b/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); } +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) { std::scoped_lock lk{m_lock}; m_conductor->LinkVsyncEvent(display_id, event); diff --git a/src/core/hle/service/vi/container.h b/src/core/hle/service/vi/container.h index 544a47889a..5d111e71ab 100644 --- a/src/core/hle/service/vi/container.h +++ b/src/core/hle/service/vi/container.h @@ -67,6 +67,7 @@ public: Result SetLayerBlending(u64 layer_id, bool enabled); 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); void LinkVsyncEvent(u64 display_id, Event* event); void UnlinkVsyncEvent(u64 display_id, Event* event);