From 07d3dabbf738ca8a547bb4aef1a8dfbceafdbba0 Mon Sep 17 00:00:00 2001 From: qiin <414382190@qq.com> Date: Fri, 12 Jun 2026 20:45:55 +0800 Subject: [PATCH 1/2] fix: keep 120hz during smart framerate streaming --- entry/src/main/ets/pages/StreamPage.ets | 37 +++- .../main/ets/service/streaming/MoonBridge.ets | 15 ++ nativelib/src/main/cpp/moonlight_bridge.cpp | 68 ++++-- nativelib/src/main/cpp/moonlight_bridge.h | 11 + nativelib/src/main/cpp/napi_init.cpp | 2 + nativelib/src/main/cpp/native_render.cpp | 202 +++++++++++++++++- nativelib/src/main/cpp/native_render.h | 26 ++- 7 files changed, 334 insertions(+), 27 deletions(-) diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index af37ed1..b0ae33d 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -747,6 +747,9 @@ struct StreamPage { InputInterceptorService.getInstance().stop(); this.inputHandler.stopMouseInterceptor(); this.stopMouseKeepAlive(); + try { + MoonBridge.setDisplayFramePacerEnabled(false); + } catch (_) {} // 停止体感助手 GyroAssistService.getInstance().dispose(); @@ -1272,25 +1275,40 @@ struct StreamPage { } /** - * 设置 XComponent 帧率 + 获取 surfaceId + 启动串流 + * 应用显示链路帧率提示。 + * 120fps 串流时启动 native DisplaySoloist,并重申 XComponent / native 层帧率范围。 */ - private async launchStream(): Promise { - const surfaceId = this.xComponentController.getXComponentSurfaceId(); - - // 设置 XComponent 期望帧率(通过 FrameNode → ArkUI_NodeHandle) - // 解决 MatePad 等设备渲染帧率被锁定在 60fps 的问题 + private applyDisplayFrameRateHints(enablePacer: boolean = true): void { const streamConfig = this.viewModel.streamConfig; if (streamConfig && streamConfig.fps > 60) { try { + if (enablePacer) { + MoonBridge.setDisplayFramePacerEnabled(true); + } const frameNode = this.getUIContext().getFrameNodeById('streamSurface'); if (frameNode) { MoonBridge.setXComponentFrameRate(frameNode, streamConfig.fps); console.info(`XComponent 帧率已设置为 ${streamConfig.fps} fps`); } + MoonBridge.refreshFrameRateHints(); } catch (e) { - console.warn(`设置 XComponent 帧率失败: ${e}`); + console.warn(`设置显示帧率提示失败: ${e}`); } + } else if (enablePacer) { + try { + MoonBridge.setDisplayFramePacerEnabled(false); + } catch (_) {} } + } + + /** + * 设置 XComponent 帧率 + 获取 surfaceId + 启动串流 + */ + private async launchStream(): Promise { + const surfaceId = this.xComponentController.getXComponentSurfaceId(); + + // 设置 XComponent / DisplaySoloist 期望帧率,解决智能帧率下 120fps 回落到 60Hz 的问题 + this.applyDisplayFrameRateHints(true); await this.viewModel.startStreaming(this.computerId, this.appId, surfaceId, this.context, this.displayGuid, this.useVdd); @@ -1362,6 +1380,7 @@ struct StreamPage { // 获取 surfaceId → 设置帧率 → 启动串流 await this.launchStream(); this.streamStartedAtMs = Date.now(); + this.applyDisplayFrameRateHints(true); // 串流就绪后再做 AI 服务可用性探测,避免与启动链路抢带宽 this.probeAiAvailableDeferred(); @@ -1458,6 +1477,9 @@ struct StreamPage { this.inputHandler.resetState(); this.inputHandler.stopMouseInterceptor(); this.stopMouseKeepAlive(); + try { + MoonBridge.setDisplayFramePacerEnabled(false); + } catch (_) {} try { pointer.setPointerVisibleSync(true); } catch (_) {} @@ -1856,6 +1878,7 @@ struct StreamPage { const screenY = globalPos.y as number; this.inputHandler.updateXComponentScreenPosition(screenX, screenY); } + this.applyDisplayFrameRateHints(this.viewModel.isConnected); }) } .width('100%') diff --git a/entry/src/main/ets/service/streaming/MoonBridge.ets b/entry/src/main/ets/service/streaming/MoonBridge.ets index eabea9a..5315fb7 100644 --- a/entry/src/main/ets/service/streaming/MoonBridge.ets +++ b/entry/src/main/ets/service/streaming/MoonBridge.ets @@ -588,6 +588,21 @@ class MoonBridgeClass { nativeLib.setXComponentFrameRate(frameNode, fps); } + /** + * 启动/停止显示层高刷保持(native DisplaySoloist)。 + * 用于 120fps 串流时持续向系统智能帧率策略请求高刷新率。 + */ + setDisplayFramePacerEnabled(enabled: boolean): void { + nativeLib.setDisplayFramePacerEnabled(enabled); + } + + /** + * 重新应用 NativeVSync / NativeWindow / DisplaySoloist 帧率提示。 + */ + refreshFrameRateHints(): void { + nativeLib.refreshFrameRateHints(); + } + // ========================================================================== // 性能模式 // ========================================================================== diff --git a/nativelib/src/main/cpp/moonlight_bridge.cpp b/nativelib/src/main/cpp/moonlight_bridge.cpp index e49068a..6793444 100644 --- a/nativelib/src/main/cpp/moonlight_bridge.cpp +++ b/nativelib/src/main/cpp/moonlight_bridge.cpp @@ -1875,13 +1875,21 @@ typedef int32_t (*PFN_GetNodeHandleFromNapiValue)(napi_env, napi_value, void** / typedef void* (*PFN_GetNativeXComponent)(void* /* ArkUI_NodeHandle */); typedef int32_t (*PFN_XCSetFrameRateOld)(void* /* OH_NativeXComponent* */, XCFrameRateRange* /* range* */); typedef int32_t (*PFN_XCSetFrameRateNew)(void* /* ArkUI_NodeHandle */, XCFrameRateRange /* range */); +typedef int32_t (*PFN_XCRegisterOnFrameCallback)( + void* /* OH_NativeXComponent* */, + void (*callback)(void* /* OH_NativeXComponent* */, uint64_t /* timestamp */, uint64_t /* targetTimestamp */)); static PFN_GetNodeHandleFromNapiValue g_pfnGetNodeHandle = nullptr; static PFN_GetNativeXComponent g_pfnGetNativeXC = nullptr; static PFN_XCSetFrameRateOld g_pfnXCSetFrameRateOld = nullptr; static PFN_XCSetFrameRateNew g_pfnXCSetFrameRateNew = nullptr; +static PFN_XCRegisterOnFrameCallback g_pfnXCRegisterOnFrame = nullptr; static bool g_xcFrameRateChecked = false; +static void XComponentOnFrameCallback(void*, uint64_t, uint64_t) { + // 空回调用于保持 XComponent/ArkUI 层有持续帧节奏提示。 +} + static void CheckAndLoadXCFrameRateApis() { if (g_xcFrameRateChecked) return; g_xcFrameRateChecked = true; @@ -1902,7 +1910,7 @@ static void CheckAndLoadXCFrameRateApis() { return; } - // 方式1 (API 20): OH_ArkUI_XComponent_SetExpectedFrameRateRange — 直接通过 NodeHandle + // 方式1 (API 20): OH_ArkUI_XComponent_SetExpectedFrameRateRange - 直接通过 NodeHandle g_pfnXCSetFrameRateNew = (PFN_XCSetFrameRateNew)dlsym(RTLD_DEFAULT, "OH_ArkUI_XComponent_SetExpectedFrameRateRange"); if (!g_pfnXCSetFrameRateNew) { @@ -1912,18 +1920,15 @@ static void CheckAndLoadXCFrameRateApis() { "OH_ArkUI_XComponent_SetExpectedFrameRateRange"); } } - if (g_pfnXCSetFrameRateNew) { - OH_LOG_INFO(LOG_APP, "XCFrameRate: API 20 OH_ArkUI_XComponent_SetExpectedFrameRateRange available"); - return; // 优先方式,不需要继续查找 - } - // 方式2 (API 12+11): OH_NativeXComponent_GetNativeXComponent + SetExpectedFrameRateRange g_pfnGetNativeXC = (PFN_GetNativeXComponent)dlsym(RTLD_DEFAULT, "OH_NativeXComponent_GetNativeXComponent"); g_pfnXCSetFrameRateOld = (PFN_XCSetFrameRateOld)dlsym(RTLD_DEFAULT, "OH_NativeXComponent_SetExpectedFrameRateRange"); + g_pfnXCRegisterOnFrame = (PFN_XCRegisterOnFrameCallback)dlsym(RTLD_DEFAULT, + "OH_NativeXComponent_RegisterOnFrameCallback"); // 回退 dlopen - if (!g_pfnGetNativeXC || !g_pfnXCSetFrameRateOld) { + if (!g_pfnGetNativeXC || !g_pfnXCSetFrameRateOld || !g_pfnXCRegisterOnFrame) { void* aceHandle = dlopen("libace_ndk.z.so", RTLD_NOW); if (aceHandle) { if (!g_pfnGetNativeXC) @@ -1932,14 +1937,23 @@ static void CheckAndLoadXCFrameRateApis() { if (!g_pfnXCSetFrameRateOld) g_pfnXCSetFrameRateOld = (PFN_XCSetFrameRateOld)dlsym(aceHandle, "OH_NativeXComponent_SetExpectedFrameRateRange"); + if (!g_pfnXCRegisterOnFrame) + g_pfnXCRegisterOnFrame = (PFN_XCRegisterOnFrameCallback)dlsym(aceHandle, + "OH_NativeXComponent_RegisterOnFrameCallback"); } } + if (g_pfnXCSetFrameRateNew) { + OH_LOG_INFO(LOG_APP, "XCFrameRate: API 20 OH_ArkUI_XComponent_SetExpectedFrameRateRange available"); + } if (g_pfnGetNativeXC && g_pfnXCSetFrameRateOld) { OH_LOG_INFO(LOG_APP, "XCFrameRate: API 12 GetNativeXComponent + API 11 SetExpectedFrameRateRange available"); } else { OH_LOG_WARN(LOG_APP, "XCFrameRate: No XComponent frame rate API available"); } + if (g_pfnGetNativeXC && g_pfnXCRegisterOnFrame) { + OH_LOG_INFO(LOG_APP, "XCFrameRate: OH_NativeXComponent_RegisterOnFrameCallback available"); + } } napi_value MoonBridge_SetXComponentFrameRate(napi_env env, napi_callback_info info) { @@ -1971,23 +1985,32 @@ napi_value MoonBridge_SetXComponentFrameRate(napi_env env, napi_callback_info in return GetUndefined(env); } + void* xComp = nullptr; + if (g_pfnGetNativeXC) { + xComp = g_pfnGetNativeXC(nodeHandle); + } + + if (xComp && g_pfnXCRegisterOnFrame) { + int32_t cbRet = g_pfnXCRegisterOnFrame(xComp, XComponentOnFrameCallback); + OH_LOG_INFO(LOG_APP, "XComponent onFrame callback registered for frame pacing: ret=%{public}d", cbRet); + } + // 方式1 (API 20): 直接通过 ArkUI_NodeHandle 设置 if (g_pfnXCSetFrameRateNew) { XCFrameRateRange range = { fps, fps, fps }; int32_t xcRet = g_pfnXCSetFrameRateNew(nodeHandle, range); - OH_LOG_INFO(LOG_APP, "XComponent FrameRate set to %{public}d fps via ArkUI_NodeHandle (API 20): ret=%{public}d", - fps, xcRet); + OH_LOG_INFO(LOG_APP, "XComponent FrameRateRange set to %{public}d/%{public}d/%{public}d fps via ArkUI_NodeHandle (API 20): ret=%{public}d", + fps, fps, fps, xcRet); return GetUndefined(env); } // 方式2 (API 12+11): NodeHandle → OH_NativeXComponent → SetExpectedFrameRateRange if (g_pfnGetNativeXC && g_pfnXCSetFrameRateOld) { - void* xComp = g_pfnGetNativeXC(nodeHandle); if (xComp) { XCFrameRateRange range = { fps, fps, fps }; int32_t xcRet = g_pfnXCSetFrameRateOld(xComp, &range); - OH_LOG_INFO(LOG_APP, "XComponent FrameRate set to %{public}d fps via NativeXComponent (API 12+11): ret=%{public}d", - fps, xcRet); + OH_LOG_INFO(LOG_APP, "XComponent FrameRateRange set to %{public}d/%{public}d/%{public}d fps via NativeXComponent (API 12+11): ret=%{public}d", + fps, fps, fps, xcRet); } else { OH_LOG_ERROR(LOG_APP, "SetXComponentFrameRate: GetNativeXComponent returned null"); } @@ -1996,4 +2019,23 @@ napi_value MoonBridge_SetXComponentFrameRate(napi_env env, napi_callback_info in OH_LOG_WARN(LOG_APP, "SetXComponentFrameRate: No API path available for fps=%{public}d", fps); return GetUndefined(env); -} \ No newline at end of file +} + +napi_value MoonBridge_SetDisplayFramePacerEnabled(napi_env env, napi_callback_info info) { + size_t argc = 1; + napi_value argv[1]; + napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr); + + bool enabled = false; + if (argc >= 1) { + napi_get_value_bool(env, argv[0], &enabled); + } + + NativeRender::GetInstance()->SetDisplayFramePacerEnabled(enabled); + return GetUndefined(env); +} + +napi_value MoonBridge_RefreshFrameRateHints(napi_env env, napi_callback_info info) { + NativeRender::GetInstance()->RefreshFrameRateHints(true); + return GetUndefined(env); +} diff --git a/nativelib/src/main/cpp/moonlight_bridge.h b/nativelib/src/main/cpp/moonlight_bridge.h index 5e2bf9e..f4e714e 100644 --- a/nativelib/src/main/cpp/moonlight_bridge.h +++ b/nativelib/src/main/cpp/moonlight_bridge.h @@ -336,6 +336,17 @@ napi_value MoonBridge_SetBassVibrationConfig(napi_env env, napi_callback_info in */ napi_value MoonBridge_SetXComponentFrameRate(napi_env env, napi_callback_info info); +/** + * 启动/停止显示层高刷保持(DisplaySoloist) + * @param enabled boolean 是否启用 + */ +napi_value MoonBridge_SetDisplayFramePacerEnabled(napi_env env, napi_callback_info info); + +/** + * 重新应用 NativeVSync / NativeWindow / DisplaySoloist 帧率提示 + */ +napi_value MoonBridge_RefreshFrameRateHints(napi_env env, napi_callback_info info); + // ============================================================================= // 常量定义 // ============================================================================= diff --git a/nativelib/src/main/cpp/napi_init.cpp b/nativelib/src/main/cpp/napi_init.cpp index e89161d..4f9b521 100644 --- a/nativelib/src/main/cpp/napi_init.cpp +++ b/nativelib/src/main/cpp/napi_init.cpp @@ -150,6 +150,8 @@ static napi_value Init(napi_env env, napi_value exports) { // XComponent 帧率设置(通过 FrameNode → ArkUI_NodeHandle,无需 libraryname) { "setXComponentFrameRate", nullptr, MoonBridge_SetXComponentFrameRate, nullptr, nullptr, nullptr, napi_default, nullptr }, + { "setDisplayFramePacerEnabled", nullptr, MoonBridge_SetDisplayFramePacerEnabled, nullptr, nullptr, nullptr, napi_default, nullptr }, + { "refreshFrameRateHints", nullptr, MoonBridge_RefreshFrameRateHints, nullptr, nullptr, nullptr, napi_default, nullptr }, }; napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); diff --git a/nativelib/src/main/cpp/native_render.cpp b/nativelib/src/main/cpp/native_render.cpp index 984d78f..5263653 100644 --- a/nativelib/src/main/cpp/native_render.cpp +++ b/nativelib/src/main/cpp/native_render.cpp @@ -19,7 +19,8 @@ * - 高帧率优化: * 1. NativeVSync SetExpectedFrameRateRange(VSync 回调频率,API 20+) * 2. NativeWindow SetFrameRateRange(Surface buffer queue 帧率偏好,API 12+) - * 3. XComponent SetExpectedFrameRateRange(ArkUI 框架层,由 MoonBridge 独立设置) + * 3. DisplaySoloist SetExpectedFrameRateRange(显示层持续 vsync 请求,API 12+) + * 4. XComponent SetExpectedFrameRateRange(ArkUI 框架层,由 MoonBridge 独立设置) */ #include "native_render.h" @@ -92,6 +93,85 @@ static bool CheckAndLoadApi20() { return g_api20Available; } +// ============================================================================= +// DisplaySoloist 动态加载(API 12+,避免低版本硬依赖) +// ============================================================================= + +struct DisplaySoloistExpectedRateRange { + int32_t min; + int32_t max; + int32_t expected; +}; + +typedef void (*PFN_DisplaySoloistFrameCallback)(long long timestamp, long long targetTimestamp, void* data); +typedef void* (*PFN_DisplaySoloistCreate)(bool useExclusiveThread); +typedef int32_t (*PFN_DisplaySoloistDestroy)(void* displaySoloist); +typedef int32_t (*PFN_DisplaySoloistStart)(void* displaySoloist, PFN_DisplaySoloistFrameCallback callback, void* data); +typedef int32_t (*PFN_DisplaySoloistStop)(void* displaySoloist); +typedef int32_t (*PFN_DisplaySoloistSetExpectedFrameRateRange)( + void* displaySoloist, DisplaySoloistExpectedRateRange* range); + +static PFN_DisplaySoloistCreate g_pfnDisplaySoloistCreate = nullptr; +static PFN_DisplaySoloistDestroy g_pfnDisplaySoloistDestroy = nullptr; +static PFN_DisplaySoloistStart g_pfnDisplaySoloistStart = nullptr; +static PFN_DisplaySoloistStop g_pfnDisplaySoloistStop = nullptr; +static PFN_DisplaySoloistSetExpectedFrameRateRange g_pfnDisplaySoloistSetRate = nullptr; +static bool g_displaySoloistChecked = false; + +static void DisplaySoloistFrameCallback(long long, long long, void*) { + // 空回调即可让 DisplaySoloist 按期望帧率持续请求 vsync。 +} + +static bool CheckAndLoadDisplaySoloistApi() { + if (g_displaySoloistChecked) { + return g_pfnDisplaySoloistCreate && g_pfnDisplaySoloistDestroy && g_pfnDisplaySoloistStart && + g_pfnDisplaySoloistStop && g_pfnDisplaySoloistSetRate; + } + g_displaySoloistChecked = true; + + g_pfnDisplaySoloistCreate = (PFN_DisplaySoloistCreate)dlsym(RTLD_DEFAULT, "OH_DisplaySoloist_Create"); + g_pfnDisplaySoloistDestroy = (PFN_DisplaySoloistDestroy)dlsym(RTLD_DEFAULT, "OH_DisplaySoloist_Destroy"); + g_pfnDisplaySoloistStart = (PFN_DisplaySoloistStart)dlsym(RTLD_DEFAULT, "OH_DisplaySoloist_Start"); + g_pfnDisplaySoloistStop = (PFN_DisplaySoloistStop)dlsym(RTLD_DEFAULT, "OH_DisplaySoloist_Stop"); + g_pfnDisplaySoloistSetRate = (PFN_DisplaySoloistSetExpectedFrameRateRange) + dlsym(RTLD_DEFAULT, "OH_DisplaySoloist_SetExpectedFrameRateRange"); + + if (!g_pfnDisplaySoloistCreate || !g_pfnDisplaySoloistDestroy || !g_pfnDisplaySoloistStart || + !g_pfnDisplaySoloistStop || !g_pfnDisplaySoloistSetRate) { + void* handle = dlopen("libnative_display_soloist.so", RTLD_NOW); + if (!handle) { + handle = dlopen("libnative_display_soloist.z.so", RTLD_NOW); + } + if (handle) { + if (!g_pfnDisplaySoloistCreate) { + g_pfnDisplaySoloistCreate = (PFN_DisplaySoloistCreate)dlsym(handle, "OH_DisplaySoloist_Create"); + } + if (!g_pfnDisplaySoloistDestroy) { + g_pfnDisplaySoloistDestroy = (PFN_DisplaySoloistDestroy)dlsym(handle, "OH_DisplaySoloist_Destroy"); + } + if (!g_pfnDisplaySoloistStart) { + g_pfnDisplaySoloistStart = (PFN_DisplaySoloistStart)dlsym(handle, "OH_DisplaySoloist_Start"); + } + if (!g_pfnDisplaySoloistStop) { + g_pfnDisplaySoloistStop = (PFN_DisplaySoloistStop)dlsym(handle, "OH_DisplaySoloist_Stop"); + } + if (!g_pfnDisplaySoloistSetRate) { + g_pfnDisplaySoloistSetRate = (PFN_DisplaySoloistSetExpectedFrameRateRange) + dlsym(handle, "OH_DisplaySoloist_SetExpectedFrameRateRange"); + } + } + } + + bool available = g_pfnDisplaySoloistCreate && g_pfnDisplaySoloistDestroy && g_pfnDisplaySoloistStart && + g_pfnDisplaySoloistStop && g_pfnDisplaySoloistSetRate; + if (available) { + OH_LOG_INFO(LOG_APP, "DisplaySoloist frame pacing API available"); + } else { + OH_LOG_WARN(LOG_APP, "DisplaySoloist frame pacing API not available"); + } + return available; +} + // ============================================================================= // 静态成员初始化 // ============================================================================= @@ -126,6 +206,7 @@ NativeRender::NativeRender() { NativeRender::~NativeRender() { OH_LOG_INFO(LOG_APP, "NativeRender destroyed"); + ReleaseDisplaySoloist(); ReleaseNativeVSync(); window_ = nullptr; surfaceReady_ = false; @@ -158,6 +239,63 @@ void NativeRender::ReleaseNativeVSync() { } } +void NativeRender::InitDisplaySoloist() { + if (displaySoloist_ != nullptr || configuredFps_ <= 60 || !displayFramePacerEnabled_.load()) { + return; + } + + if (!CheckAndLoadDisplaySoloistApi()) { + return; + } + + displaySoloist_ = g_pfnDisplaySoloistCreate(true); + if (!displaySoloist_) { + OH_LOG_WARN(LOG_APP, "DisplaySoloist create failed"); + return; + } + + ApplyDisplaySoloistFrameRate(); + int32_t ret = g_pfnDisplaySoloistStart(displaySoloist_, DisplaySoloistFrameCallback, this); + if (ret == 0) { + displaySoloistStarted_ = true; + OH_LOG_INFO(LOG_APP, "DisplaySoloist started for %{public}d fps keepalive", configuredFps_); + } else { + OH_LOG_WARN(LOG_APP, "DisplaySoloist start failed: ret=%{public}d", ret); + g_pfnDisplaySoloistDestroy(displaySoloist_); + displaySoloist_ = nullptr; + displaySoloistStarted_ = false; + } +} + +void NativeRender::ReleaseDisplaySoloist() { + if (displaySoloist_ == nullptr) { + return; + } + + if (displaySoloistStarted_ && g_pfnDisplaySoloistStop) { + g_pfnDisplaySoloistStop(displaySoloist_); + } + if (g_pfnDisplaySoloistDestroy) { + g_pfnDisplaySoloistDestroy(displaySoloist_); + } + displaySoloist_ = nullptr; + displaySoloistStarted_ = false; + OH_LOG_INFO(LOG_APP, "DisplaySoloist stopped"); +} + +void NativeRender::SetDisplayFramePacerEnabled(bool enable) { + displayFramePacerEnabled_.store(enable); + OH_LOG_INFO(LOG_APP, "Display frame pacer %{public}s for configured fps=%{public}d", + enable ? "enabled" : "disabled", configuredFps_); + + if (enable && configuredFps_ > 60) { + InitDisplaySoloist(); + RefreshFrameRateHints(true); + } else { + ReleaseDisplaySoloist(); + } +} + // ============================================================================= // NativeWindow 管理 // ============================================================================= @@ -177,7 +315,7 @@ void NativeRender::SetNativeWindow(OHNativeWindow* window, uint64_t width, uint6 // 如果帧率已配置,立即应用帧率范围 // 这处理 SetConfiguredFps 在 SetNativeWindow 之前调用的情况 if (configuredFps_ > 0 && nativeVSync_ != nullptr) { - ApplyFrameRateRange(); + RefreshFrameRateHints(true); } surfaceReady_ = true; @@ -185,6 +323,7 @@ void NativeRender::SetNativeWindow(OHNativeWindow* window, uint64_t width, uint6 static_cast(window), width, height); } else { surfaceReady_ = false; + ReleaseDisplaySoloist(); ReleaseNativeVSync(); OH_LOG_INFO(LOG_APP, "NativeWindow cleared"); } @@ -202,6 +341,13 @@ void NativeRender::SetConfiguredFps(int fps) { // 应用帧率范围(NativeWindow/Surface 层) ApplyNativeWindowFrameRate(); + + if (displayFramePacerEnabled_.load() && configuredFps_ > 60) { + ReleaseDisplaySoloist(); + InitDisplaySoloist(); + } else if (configuredFps_ <= 60) { + ReleaseDisplaySoloist(); + } } void NativeRender::SetVsyncEnabled(bool enable) { @@ -226,7 +372,7 @@ void NativeRender::ConfigureNativeWindow() { // 如果帧率已配置,立即在 NativeWindow 层设置帧率偏好 if (configuredFps_ > 60) { - ApplyNativeWindowFrameRate(); + RefreshFrameRateHints(true); } } @@ -279,16 +425,35 @@ void NativeRender::ApplyNativeWindowFrameRate() { return; } - // strategy = 0 (DEFAULT): 让系统根据能力选择最佳刷新率 - int32_t ret = g_pfnNWSetFrameRateRange(window_, configuredFps_, configuredFps_, configuredFps_, 0); + // strategy = 1 (EXACT): 高帧率串流时明确请求固定刷新率,避免智能帧率回落到 60Hz。 + constexpr int32_t NATIVE_WINDOW_FRAME_RATE_STRATEGY_EXACT = 1; + int32_t ret = g_pfnNWSetFrameRateRange( + window_, configuredFps_, configuredFps_, configuredFps_, NATIVE_WINDOW_FRAME_RATE_STRATEGY_EXACT); if (ret == 0) { - OH_LOG_INFO(LOG_APP, "NativeWindow FrameRateRange set to %{public}d fps (Surface level)", configuredFps_); + OH_LOG_INFO(LOG_APP, "NativeWindow FrameRateRange set to %{public}d/%{public}d/%{public}d fps (EXACT)", + configuredFps_, configuredFps_, configuredFps_); } else { OH_LOG_WARN(LOG_APP, "NativeWindow SetFrameRateRange failed: ret=%{public}d, fps=%{public}d", ret, configuredFps_); } } +void NativeRender::ApplyDisplaySoloistFrameRate() { + if (displaySoloist_ == nullptr || configuredFps_ <= 60 || !g_pfnDisplaySoloistSetRate) { + return; + } + + DisplaySoloistExpectedRateRange range = { configuredFps_, configuredFps_, configuredFps_ }; + int32_t ret = g_pfnDisplaySoloistSetRate(displaySoloist_, &range); + if (ret == 0) { + OH_LOG_INFO(LOG_APP, "DisplaySoloist FrameRateRange set to %{public}d/%{public}d/%{public}d fps", + configuredFps_, configuredFps_, configuredFps_); + } else { + OH_LOG_WARN(LOG_APP, "DisplaySoloist SetExpectedFrameRateRange failed: ret=%{public}d, fps=%{public}d", + ret, configuredFps_); + } +} + void NativeRender::ApplyFrameRateRange() { // NativeVSync SetExpectedFrameRateRange (API 20+) // 设置 VSync 回调的期望帧率,影响 VSync 信号频率 @@ -310,6 +475,30 @@ void NativeRender::ApplyFrameRateRange() { } } +void NativeRender::RefreshFrameRateHints(bool force) { + if (configuredFps_ <= 60) { + return; + } + + auto now = std::chrono::steady_clock::now(); + if (!force && (now - lastFrameRateHintTime_) < std::chrono::seconds(2)) { + return; + } + lastFrameRateHintTime_ = now; + + OH_LOG_INFO(LOG_APP, "Refreshing frame rate hints: configured fps=%{public}d", configuredFps_); + ApplyFrameRateRange(); + ApplyNativeWindowFrameRate(); + + if (displayFramePacerEnabled_.load()) { + if (displaySoloist_ == nullptr) { + InitDisplaySoloist(); + } else { + ApplyDisplaySoloistFrameRate(); + } + } +} + // ============================================================================= // 帧呈现时间计算(VSync 模式) // ============================================================================= @@ -355,6 +544,7 @@ int64_t NativeRender::CalculatePresentTime(int64_t pts) const { void NativeRender::SubmitFrame(OH_AVCodec* codec, uint32_t bufferIndex, int64_t pts, int64_t enqueueTimeMs) { int32_t renderResult; + RefreshFrameRateHints(false); if (vsyncEnabled_.load()) { // VSync 模式:使用 RenderOutputBufferAtTime 精确控制呈现时间 diff --git a/nativelib/src/main/cpp/native_render.h b/nativelib/src/main/cpp/native_render.h index 45d051e..a4622ee 100644 --- a/nativelib/src/main/cpp/native_render.h +++ b/nativelib/src/main/cpp/native_render.h @@ -19,7 +19,8 @@ * - 高帧率优化: * 1. NativeVSync SetExpectedFrameRateRange(VSync 回调频率,API 20+) * 2. NativeWindow SetFrameRateRange(Surface buffer queue 帧率偏好,API 12+) - * 3. XComponent SetExpectedFrameRateRange(ArkUI 框架层,由 MoonBridge 独立设置) + * 3. DisplaySoloist SetExpectedFrameRateRange(显示层持续 vsync 请求,API 12+) + * 4. XComponent SetExpectedFrameRateRange(ArkUI 框架层,由 MoonBridge 独立设置) */ #ifndef NATIVE_RENDER_H @@ -71,6 +72,16 @@ class NativeRender { * 获取配置的帧率 */ int GetConfiguredFps() const { return configuredFps_; } + + /** + * 启动/停止显示高刷保持(DisplaySoloist) + */ + void SetDisplayFramePacerEnabled(bool enable); + + /** + * 重新应用各 native 层帧率提示(用于前后台/Surface 恢复/智能帧率保活) + */ + void RefreshFrameRateHints(bool force = false); /** * 启用/禁用 VSync 渲染模式 @@ -125,6 +136,13 @@ class NativeRender { // 释放 NativeVSync void ReleaseNativeVSync(); + // 初始化/释放 DisplaySoloist + void InitDisplaySoloist(); + void ReleaseDisplaySoloist(); + + // 应用 DisplaySoloist 帧率(显示层持续 vsync 请求,API 12+) + void ApplyDisplaySoloistFrameRate(); + private: // 单例 static NativeRender* instance_; @@ -138,6 +156,7 @@ class NativeRender { // 帧率配置 int configuredFps_ = 60; + std::atomic displayFramePacerEnabled_{false}; // VSync 模式 std::atomic vsyncEnabled_{false}; @@ -152,9 +171,14 @@ class NativeRender { // NativeVSync(用于设置期望帧率范围,API 20+) OH_NativeVSync* nativeVSync_ = nullptr; + + // DisplaySoloist(用于智能帧率下持续请求高刷新率,API 12+,动态加载) + void* displaySoloist_ = nullptr; + bool displaySoloistStarted_ = false; // 上一帧渲染时间(用于帧率控制) std::chrono::steady_clock::time_point lastFrameTime_; + std::chrono::steady_clock::time_point lastFrameRateHintTime_; }; #endif // NATIVE_RENDER_H From 57ba824dd79469f86f6f090f0c9ae3e7001373a5 Mon Sep 17 00:00:00 2001 From: qiin <414382190@qq.com> Date: Fri, 12 Jun 2026 21:25:44 +0800 Subject: [PATCH 2/2] fix: address frame pacing review feedback --- entry/src/main/ets/pages/StreamPage.ets | 9 ++- nativelib/src/main/cpp/moonlight_bridge.cpp | 5 +- nativelib/src/main/cpp/native_render.cpp | 63 ++++++++++++++++----- nativelib/src/main/cpp/native_render.h | 7 +++ 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index b0ae33d..1007749 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -1310,7 +1310,14 @@ struct StreamPage { // 设置 XComponent / DisplaySoloist 期望帧率,解决智能帧率下 120fps 回落到 60Hz 的问题 this.applyDisplayFrameRateHints(true); - await this.viewModel.startStreaming(this.computerId, this.appId, surfaceId, this.context, this.displayGuid, this.useVdd); + try { + await this.viewModel.startStreaming(this.computerId, this.appId, surfaceId, this.context, this.displayGuid, this.useVdd); + } catch (err) { + try { + MoonBridge.setDisplayFramePacerEnabled(false); + } catch (_) {} + throw err instanceof Error ? err : new Error(String(err)); + } // 串流启动后调度未识别 USB 手柄检测(延迟内 GCK 完成扫描) this.scheduleUnhandledUsbDetection(); diff --git a/nativelib/src/main/cpp/moonlight_bridge.cpp b/nativelib/src/main/cpp/moonlight_bridge.cpp index 6793444..1e8e954 100644 --- a/nativelib/src/main/cpp/moonlight_bridge.cpp +++ b/nativelib/src/main/cpp/moonlight_bridge.cpp @@ -2001,7 +2001,10 @@ napi_value MoonBridge_SetXComponentFrameRate(napi_env env, napi_callback_info in int32_t xcRet = g_pfnXCSetFrameRateNew(nodeHandle, range); OH_LOG_INFO(LOG_APP, "XComponent FrameRateRange set to %{public}d/%{public}d/%{public}d fps via ArkUI_NodeHandle (API 20): ret=%{public}d", fps, fps, fps, xcRet); - return GetUndefined(env); + if (xcRet == 0) { + return GetUndefined(env); + } + OH_LOG_WARN(LOG_APP, "XComponent API 20 frame rate path failed, trying NativeXComponent fallback"); } // 方式2 (API 12+11): NodeHandle → OH_NativeXComponent → SetExpectedFrameRateRange diff --git a/nativelib/src/main/cpp/native_render.cpp b/nativelib/src/main/cpp/native_render.cpp index 5263653..53f018b 100644 --- a/nativelib/src/main/cpp/native_render.cpp +++ b/nativelib/src/main/cpp/native_render.cpp @@ -240,6 +240,7 @@ void NativeRender::ReleaseNativeVSync() { } void NativeRender::InitDisplaySoloist() { + std::lock_guard lock(frameRateMutex_); if (displaySoloist_ != nullptr || configuredFps_ <= 60 || !displayFramePacerEnabled_.load()) { return; } @@ -268,6 +269,7 @@ void NativeRender::InitDisplaySoloist() { } void NativeRender::ReleaseDisplaySoloist() { + std::lock_guard lock(frameRateMutex_); if (displaySoloist_ == nullptr) { return; } @@ -284,6 +286,7 @@ void NativeRender::ReleaseDisplaySoloist() { } void NativeRender::SetDisplayFramePacerEnabled(bool enable) { + std::lock_guard lock(frameRateMutex_); displayFramePacerEnabled_.store(enable); OH_LOG_INFO(LOG_APP, "Display frame pacer %{public}s for configured fps=%{public}d", enable ? "enabled" : "disabled", configuredFps_); @@ -293,6 +296,7 @@ void NativeRender::SetDisplayFramePacerEnabled(bool enable) { RefreshFrameRateHints(true); } else { ReleaseDisplaySoloist(); + ResetFrameRateHintsToDefault(); } } @@ -301,6 +305,7 @@ void NativeRender::SetDisplayFramePacerEnabled(bool enable) { // ============================================================================= void NativeRender::SetNativeWindow(OHNativeWindow* window, uint64_t width, uint64_t height) { + std::lock_guard lock(frameRateMutex_); window_ = window; surfaceWidth_ = width; surfaceHeight_ = height; @@ -330,6 +335,8 @@ void NativeRender::SetNativeWindow(OHNativeWindow* window, uint64_t width, uint6 } void NativeRender::SetConfiguredFps(int fps) { + std::lock_guard lock(frameRateMutex_); + int oldFps = configuredFps_; configuredFps_ = fps; OH_LOG_INFO(LOG_APP, "Configured FPS set to: %{public}d", fps); @@ -347,6 +354,9 @@ void NativeRender::SetConfiguredFps(int fps) { InitDisplaySoloist(); } else if (configuredFps_ <= 60) { ReleaseDisplaySoloist(); + if (oldFps > 60) { + ResetFrameRateHintsToDefault(); + } } } @@ -360,6 +370,7 @@ void NativeRender::SetVsyncEnabled(bool enable) { } void NativeRender::ConfigureNativeWindow() { + std::lock_guard lock(frameRateMutex_); if (window_ == nullptr) { return; } @@ -417,7 +428,12 @@ static bool CheckAndLoadNWFrameRateApi() { } void NativeRender::ApplyNativeWindowFrameRate() { - if (window_ == nullptr || configuredFps_ <= 60) { + std::lock_guard lock(frameRateMutex_); + ApplyNativeWindowFrameRateValue(configuredFps_, true); +} + +void NativeRender::ApplyNativeWindowFrameRateValue(int fps, bool exact) { + if (window_ == nullptr || fps <= 0) { return; } @@ -427,55 +443,74 @@ void NativeRender::ApplyNativeWindowFrameRate() { // strategy = 1 (EXACT): 高帧率串流时明确请求固定刷新率,避免智能帧率回落到 60Hz。 constexpr int32_t NATIVE_WINDOW_FRAME_RATE_STRATEGY_EXACT = 1; + constexpr int32_t NATIVE_WINDOW_FRAME_RATE_STRATEGY_DEFAULT = 0; + int32_t strategy = exact ? NATIVE_WINDOW_FRAME_RATE_STRATEGY_EXACT : NATIVE_WINDOW_FRAME_RATE_STRATEGY_DEFAULT; int32_t ret = g_pfnNWSetFrameRateRange( - window_, configuredFps_, configuredFps_, configuredFps_, NATIVE_WINDOW_FRAME_RATE_STRATEGY_EXACT); + window_, fps, fps, fps, strategy); if (ret == 0) { - OH_LOG_INFO(LOG_APP, "NativeWindow FrameRateRange set to %{public}d/%{public}d/%{public}d fps (EXACT)", - configuredFps_, configuredFps_, configuredFps_); + OH_LOG_INFO(LOG_APP, "NativeWindow FrameRateRange set to %{public}d/%{public}d/%{public}d fps (%{public}s)", + fps, fps, fps, exact ? "EXACT" : "DEFAULT"); } else { OH_LOG_WARN(LOG_APP, "NativeWindow SetFrameRateRange failed: ret=%{public}d, fps=%{public}d", - ret, configuredFps_); + ret, fps); } } void NativeRender::ApplyDisplaySoloistFrameRate() { - if (displaySoloist_ == nullptr || configuredFps_ <= 60 || !g_pfnDisplaySoloistSetRate) { + std::lock_guard lock(frameRateMutex_); + ApplyDisplaySoloistFrameRateValue(configuredFps_); +} + +void NativeRender::ApplyDisplaySoloistFrameRateValue(int fps) { + if (displaySoloist_ == nullptr || fps <= 60 || !g_pfnDisplaySoloistSetRate) { return; } - DisplaySoloistExpectedRateRange range = { configuredFps_, configuredFps_, configuredFps_ }; + DisplaySoloistExpectedRateRange range = { fps, fps, fps }; int32_t ret = g_pfnDisplaySoloistSetRate(displaySoloist_, &range); if (ret == 0) { OH_LOG_INFO(LOG_APP, "DisplaySoloist FrameRateRange set to %{public}d/%{public}d/%{public}d fps", - configuredFps_, configuredFps_, configuredFps_); + fps, fps, fps); } else { OH_LOG_WARN(LOG_APP, "DisplaySoloist SetExpectedFrameRateRange failed: ret=%{public}d, fps=%{public}d", - ret, configuredFps_); + ret, fps); } } void NativeRender::ApplyFrameRateRange() { + std::lock_guard lock(frameRateMutex_); + ApplyFrameRateRangeValue(configuredFps_); +} + +void NativeRender::ApplyFrameRateRangeValue(int fps) { // NativeVSync SetExpectedFrameRateRange (API 20+) // 设置 VSync 回调的期望帧率,影响 VSync 信号频率 // 注意:XComponent 帧率提示由 MoonBridge_SetXComponentFrameRate 通过 ArkUI_NodeHandle 独立设置 if (nativeVSync_ != nullptr && CheckAndLoadApi20()) { OH_NativeVSync_ExpectedRateRange range; - range.min = configuredFps_; - range.max = configuredFps_; - range.expected = configuredFps_; + range.min = fps; + range.max = fps; + range.expected = fps; int32_t ret = g_pfnSetExpectedFrameRateRange(nativeVSync_, &range); if (ret == 0) { OH_LOG_INFO(LOG_APP, "NativeVSync FrameRateRange set to fixed %{public}d fps", - configuredFps_); + fps); } else { OH_LOG_WARN(LOG_APP, "Failed to set NativeVSync FrameRateRange to %{public}d: ret=%{public}d", - configuredFps_, ret); + fps, ret); } } } +void NativeRender::ResetFrameRateHintsToDefault() { + OH_LOG_INFO(LOG_APP, "Resetting frame rate hints to default 60 fps"); + ApplyFrameRateRangeValue(60); + ApplyNativeWindowFrameRateValue(60, false); +} + void NativeRender::RefreshFrameRateHints(bool force) { + std::lock_guard lock(frameRateMutex_); if (configuredFps_ <= 60) { return; } diff --git a/nativelib/src/main/cpp/native_render.h b/nativelib/src/main/cpp/native_render.h index a4622ee..2fbb098 100644 --- a/nativelib/src/main/cpp/native_render.h +++ b/nativelib/src/main/cpp/native_render.h @@ -126,9 +126,11 @@ class NativeRender { // 应用帧率范围(通过 NativeVSync,API 20+) void ApplyFrameRateRange(); + void ApplyFrameRateRangeValue(int fps); // 应用 NativeWindow 帧率(Surface buffer queue 级别,API 12+) void ApplyNativeWindowFrameRate(); + void ApplyNativeWindowFrameRateValue(int fps, bool exact); // 初始化 NativeVSync void InitNativeVSync(); @@ -142,6 +144,10 @@ class NativeRender { // 应用 DisplaySoloist 帧率(显示层持续 vsync 请求,API 12+) void ApplyDisplaySoloistFrameRate(); + void ApplyDisplaySoloistFrameRateValue(int fps); + + // 将 NativeVSync / NativeWindow 帧率提示恢复到 60Hz 默认值 + void ResetFrameRateHintsToDefault(); private: // 单例 @@ -149,6 +155,7 @@ class NativeRender { static std::mutex instanceMutex_; // Surface 相关 + std::recursive_mutex frameRateMutex_; OHNativeWindow* window_ = nullptr; uint64_t surfaceWidth_ = 0; uint64_t surfaceHeight_ = 0;