Skip to content

FEATURE: Interpolation Pacer#1137

Open
JeodC wants to merge 1 commit into
Kenix3:port-maintenancefrom
JeodC:interpolation-pacer
Open

FEATURE: Interpolation Pacer#1137
JeodC wants to merge 1 commit into
Kenix3:port-maintenancefrom
JeodC:interpolation-pacer

Conversation

@JeodC

@JeodC JeodC commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

The current interpolation system takes a requested rate from the port and meets the demand, even if it sacrifices performance. This is more easily noticeable in Lighthouse with maximum draw distance. While some strong single-core processors will have zero problem, others designed for multi-core will run into lower framerate due to the way interpolation works.

Lighthouse has a version of this pull request already, called AdaptiveFPS. This pull request moves that mechanism to libultraship so it can be shared with other ports. It adds a small, self-contained Fast::InterpolationPacer that decides how many interpolated sub-frames to render between two fixed-rate game ticks, bounded by the measured render budget. It is additive and opt-in: a port adopts it by feeding Plan() in place of its raw accumulator. Ports that don't adopt it are unaffected.

Given the user's target FPS, the game's native tick rate, and per-tick measurements of game-logic time and per-sub-frame render cost, it returns how many sub-frames fit the tick's render budget (after reserving logic time), plus the present-pace FPS to feed
Window::SetTargetFps. A heavy scene falls back toward the native rate (keyframe + as many tweens as fit) instead of multiplying render cost across tweens. Game speed stays correct; only smoothness degrades under load.

Design Notes

  • nativeLogicFps is host-supplied, no baked-in rate. Native tick differs per game (20 Hz OoT/MM, 30 Hz SM64/MK64/BK) and can vary per tick (pause/cutscene).
  • Cost is reported by the host, not measured internally: the host drives the render loop with game-specific interleaving, so it times its own submission.
  • SetSmoothness() optionally exposes the budget safety factor; the default is sensible and most ports won't surface it.
  • Backend-agnostic: pure CPU pacing math, no gfx API calls.
  • Self-contained with only <chrono>, <cstdint>, <algorithm>. No new dependencies.

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.

1 participant