Browse Source

add LayerIsOverlay to run overlay on it's own framerate, unstub overlay notification count and sync emu settings when setting language (so it actually changes)

pull/3123/head
Maufeat 3 weeks ago
parent
commit
a2d9a9fd48
  1. 10
      src/core/hle/service/am/display_layer_manager.cpp
  2. 4
      src/core/hle/service/am/window_system.cpp
  3. 9
      src/core/hle/service/friend/friend.cpp
  4. 3
      src/core/hle/service/nvnflinger/display.h
  5. 63
      src/core/hle/service/nvnflinger/hardware_composer.cpp
  6. 4
      src/core/hle/service/nvnflinger/hardware_composer.h
  7. 10
      src/core/hle/service/nvnflinger/surface_flinger.cpp
  8. 1
      src/core/hle/service/nvnflinger/surface_flinger.h
  9. 67
      src/core/hle/service/set/system_settings_server.cpp
  10. 6
      src/core/hle/service/vi/conductor.cpp
  11. 10
      src/core/hle/service/vi/container.cpp
  12. 1
      src/core/hle/service/vi/container.h

10
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();

4
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) {

9
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");

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

@ -15,7 +15,7 @@ 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) {}
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 {

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) {
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).
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<std::pair<Layer*, Framebuffer*>> 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;

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-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};
};

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-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) {

1
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<Layer> FindLayer(s32 consumer_binder_id);

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

@ -36,6 +36,38 @@ struct SettingsHeader {
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::size_t>(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<u32> 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<s32> out_count, OutBuffer<B
}
void ISystemSettingsServer::SetupSettings() {
auto system_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
auto system_dir =
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
if (!LoadSettingsFile(system_dir, []() { return DefaultSystemSettings(); })) {
ASSERT(false);
}
auto private_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000052";
auto private_dir =
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000052";
if (!LoadSettingsFile(private_dir, []() { return DefaultPrivateSettings(); })) {
ASSERT(false);
}
auto device_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000053";
auto device_dir =
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000053";
if (!LoadSettingsFile(device_dir, []() { return DefaultDeviceSettings(); })) {
ASSERT(false);
}
auto appln_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000054";
auto appln_dir =
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000054";
if (!LoadSettingsFile(appln_dir, []() { return DefaultApplnSettings(); })) {
ASSERT(false);
}
}
void ISystemSettingsServer::StoreSettings() {
auto system_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
auto system_dir =
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000050";
if (!StoreSettingsFile(system_dir, m_system_settings)) {
LOG_ERROR(Service_SET, "Failed to store System settings");
}
auto private_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000052";
auto private_dir =
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000052";
if (!StoreSettingsFile(private_dir, m_private_settings)) {
LOG_ERROR(Service_SET, "Failed to store Private settings");
}
auto device_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000053";
auto device_dir =
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000053";
if (!StoreSettingsFile(device_dir, m_device_settings)) {
LOG_ERROR(Service_SET, "Failed to store Device settings");
}
auto appln_dir = Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000054";
auto appln_dir =
Common::FS::GetEdenPath(Common::FS::EdenPath::NANDDir) / "system/save/8000000000000054";
if (!StoreSettingsFile(appln_dir, m_appln_settings)) {
LOG_ERROR(Service_SET, "Failed to store ApplLn settings");
}
@ -1398,8 +1442,9 @@ void ISystemSettingsServer::StoreSettingsThreadFunc(std::stop_token stop_token)
while (Common::StoppableTimedWait(stop_token, std::chrono::minutes(1))) {
std::scoped_lock l{m_save_needed_mutex};
if (!std::exchange(m_save_needed, false))
if (!std::exchange(m_save_needed, false)) {
continue;
}
StoreSettings();
}
}

6
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-License-Identifier: GPL-2.0-or-later
@ -107,7 +110,8 @@ s64 Conductor::GetNextTicks() const {
speed_scale = 1.f;
}
const f32 effective_fps = 60.f / static_cast<f32>(m_swap_interval);
const s32 vsync_interval = 1;
const f32 effective_fps = 60.f / static_cast<f32>(vsync_interval);
return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
}

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);
}
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);

1
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);

Loading…
Cancel
Save