Skip to content

fix: Normalize js_refresh_rate against actual screen fps - #1464

Merged
sbarrio merged 2 commits into
developfrom
sbarrio/fix/refresh-rate-normalization-against-current-screen-fps
Oct 5, 2026
Merged

sbarrio merged 2 commits into
developfrom
sbarrio/fix/refresh-rate-normalization-against-current-screen-fps

Conversation

@sbarrio

@sbarrio sbarrio commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

What does this PR do?

This PR fixes the js_refresh_rate normalization. It normalizes fps metrics into a 0-60fps range, but it always did so against the screen's maximum possible refresh rate rather than the rate the screen is actually running at. On high refresh rate screens running below their maximum (e.g. a 120Hz panel running at 60Hz), a healthy JS thread was reported at ~30fps.

The current refresh rate now comes from CADisplayLink's targetTimestamp on iOS and from the default display's Display.refreshRate on Android, and is passed to the existing normalization functions in place of the maximum.

Android below API 30: we keep normalizing against the maximum refresh rate. Before API 30 Display.refreshRate isn't cached and every read is an IPC to the system, which we don't want on every JS frame (dd-sdk-android doesn't read it below API 30 either).

Motivation

The calculation of js_refresh_rate should be accurate under all circumstances.

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)
  • If this PR is auto-generated, please make sure also to manually update the code related to the change

@sbarrio
sbarrio marked this pull request as ready for review October 2, 2026 09:43
@sbarrio
sbarrio requested a review from a team as a code owner October 2, 2026 09:43
Copilot AI balanced review requested due to automatic review settings October 2, 2026 09:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Android may use the wrong display on multi-display devices, and the iOS refresh-rate calculation lacks direct coverage.

Review effort: Balanced
Findings: 1 Medium severity · 1 Low severity

Open (2)
What changed in this PR

Updates JS refresh-rate normalization to use the display’s active refresh rate rather than its maximum supported rate.

Changes:

  • Propagates current display FPS through Android and iOS frame callbacks.
  • Adds normalization tests for 60 Hz and 120 Hz scenarios.
  • Uses platform-specific refresh-rate APIs.
File Description
packages/​core/​ios/​Tests/​DdSdkTests.swift Tests FPS-aware normalization.
packages/​core/​ios/​Sources/​JSRefreshRateListener.swift Derives FPS from display-link timestamps.
packages/​core/​ios/​Sources/​DdSdkImplementation.swift Applies reported FPS during normalization.
packages/​core/​android/​src/​test/​kotlin/​com/​datadog/​reactnative/​DdSdkTest.kt Tests dynamic refresh-rate normalization.
packages/​core/​android/​src/​main/​kotlin/​com/​datadog/​reactnative/​FrameRateProvider.kt Reports the display refresh rate with frame timing.
packages/​core/​android/​src/​main/​kotlin/​com/​datadog/​reactnative/​DdSdkImplementation.kt Supplies the display and applies its refresh rate.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/core/ios/Sources/JSRefreshRateListener.swift
Copilot AI balanced review requested due to automatic review settings October 2, 2026 10:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The Android implementation has avoidable per-frame overhead and does not meet the stated behavior on supported pre-30 devices.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (2)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Avoid reading display refresh rate when frame vitals are disabled

packages/​core/​android/​src/​main/​kotlin/​com/​datadog/​reactnative/​DdSdkImplementation.kt:340

The display is also supplied when frame-rate vitals are disabled but JavaScript long-task monitoring is enabled. In that supported configuration, FpsFrameCallback now reads display.refreshRate on every JS-thread frame even though buildFrameTimeCallback discards the value, adding continuous overhead unrelated to the enabled feature. Only resolve/pass the display when the vitals frequency is not NEVER.

@marco-saia-datadog marco-saia-datadog left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍

@sbarrio
sbarrio merged commit 3efe108 into develop Oct 5, 2026
16 checks passed
@sbarrio
sbarrio deleted the sbarrio/fix/refresh-rate-normalization-against-current-screen-fps branch October 5, 2026 08:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants