Skip to content

gpu: keep GPU tick timestamps for Mario Kart World - #870

Merged
rdenno merged 2 commits into
Eglantine-Community:nx2-experimentalfrom
rdenno:fix/mkw-legacy-gpu-timestamps
Nov 14, 2026
Merged

rdenno merged 2 commits into
Eglantine-Community:nx2-experimentalfrom
rdenno:fix/mkw-legacy-gpu-timestamps

Conversation

@rdenno

@rdenno rdenno commented Nov 14, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Since #794, Switch 2 titles read report timestamps as real nanoseconds (ReadGpuTimestamp in gpu_timestamp.h). Before, GetTicks() returned 614.4 MHz GPU ticks divided by the GPU clock setting (256 with the default Boost).

Mario Kart World sizes its dynamic resolution from these timestamps. With nanoseconds, the emulator's GPU latency looks like a nearly full frame budget, so the game changes its resolution every few frames (render targets switch between about 336x188 and 352x200 at the same address). Each change makes the texture cache recreate and re-upload those render targets. In a race that's about 48,000 image creations per 2,600 frames, against about 3,000 before #794, and it all lands on the GPU back thread.

Fix

Mario Kart World (0400C3F00006E000) keeps the previous timestamps: the existing non-ampere path, GPU ticks over the clock setting's divisor. Other Switch 2 titles are unchanged. The title is looked up once the application process exists and cached per GPU instance; before that, nanoseconds are used as now.

Testing

Time trial on Whistlestop Summit, the same course in every run, fps over the same 1,800 race frames. RX 6700S (AMDVLK, Windows), default settings:

Build fps
Before #794 (db61d0de3) 27.1, 28.1, 28.2
nx2-experimental (28edf9fd1) 19.7
nx2-experimental + this PR 30.2, 30.1

Setting the GPU clock to Overclock on unpatched builds also restores about 30 fps, which confirms the cause. Frames looked normal in the patched runs.

I added a unit test to src/tests/video_core/gpu_timestamp.cpp, but haven't compiled or run it: the test build is off on the machine I used.

Not addressed

Any title that changes its dynamic resolution still pays the render-target recreation cost in the texture cache. Making those resizes cheaper would be a separate change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YQZoXmd1tepstZpngXBzCj

Summary by CodeRabbit

  • Bug Fixes
    • Improved GPU timing compatibility for Mario Kart World by preserving its legacy timestamp behavior.
    • The special handling applies only to the specified title; other titles continue to use the existing timestamp behavior. No title-specific legacy handling is applied when no game is running.
    • Added coverage for the title-specific behavior and for timestamp conversion when legacy handling is not active.

Since Eglantine-Community#794, Switch 2 titles read report timestamps as nanoseconds
(ReadGpuTimestamp). Mario Kart World sizes its dynamic resolution from
GPU timestamps: with nanoseconds the emulator's GPU latency reads as a
nearly full frame budget, so the resolution changes every few frames and
each change recreates and re-uploads its render targets (16x the texture
cache image creations of the previous behaviour).

Keep the previous timestamps (GPU ticks over the GPU clock setting's
divisor) for Mario Kart World only; other Switch 2 titles are unchanged.

Whistlestop Summit time trial, RX 6700S, default settings:
before Eglantine-Community#794 27-28 fps, nx2-experimental 19.7 fps, with this 30.1-30.2 fps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YQZoXmd1tepstZpngXBzCj
@coderabbitai

coderabbitai Bot commented Nov 14, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7d1e6a17-210b-4dba-b506-9d1b876776d5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review

No actionable comments were generated in the recent review.

Walkthrough

The GPU timestamp path now checks the running title’s program ID and disables Ampere timestamp handling for Mario Kart World. Tests cover title classification and non-Ampere timestamp scaling.

Changes: Legacy Ampere timestamps

Layer / File(s) Summary
Title-specific timestamp selection
src/video_core/gpu_timestamp.h, src/video_core/gpu.cpp, src/tests/video_core/gpu_timestamp.cpp
UsesLegacyAmpereTimestamps matches Mario Kart World’s program ID. The GPU caches the title check after a program ID becomes available and disables Ampere timestamp handling for that title. Tests cover title classification and non-Ampere timestamp scaling.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: a-man-with-a-plan-dev

Merge Risk: Minimal · previous review 9387b

This change keeps the previous GPU tick timestamps for Mario Kart World and leaves other titles on nanosecond timestamps. No actionable merge-blocking risk was found. The author should still build and run the new test before merging.

Security architecture risk: Low · up to 9387b

A title-specific timestamp choice can survive an in-session game change and affect the next game’s timing. The identified concern is limited to timing-policy lifetime; no new permissions or data-access authority were established.

Retained concerns

  • Low · architecture · inferred: The title-specific timestamp policy is cached for the GPU lifetime rather than the application identity lifetime. Existing in-session application creation and termination paths can replace the active process, but the cached classification has no identity check or invalidation. If Ampere mode remains active, a classification established for Mario Kart World can therefore select legacy units for a later title; a previously cached nonlegacy classification can also prevent the intended exception. Full system reload clears the cache, but the inspected live-transition paths do not.
Security review details

Security Blast Radius

  • inferred — The demonstrated effect is within an emulator session's guest-visible GPU report timestamps. Because the cache is shared by that GPU instance, its policy can affect later applications sharing the instance, rather than only the application that established it.

Security Findings and Attack Paths

  • inferred — Guest-triggered reports already reached GetTicks before this change. The inspected change adds a numeric mode selector, not a new report-writing capability. No introduced privilege escalation, expanded write authority, or data-disclosure path was established.

Trust Boundaries and Controls

  • observed — Active application identity remains kernel-owned, but the existing application-management path can replace it during a session. The new GPU cache does not observe that ownership transition; its atomic storage is not an identity-validity control.

Resilience and Maintainability Implications

  • observed — Normal shutdown requests and joins the GPU thread before GPU destruction and kernel application-process release. This ordering is counterevidence against a new shutdown-time process dereference race on the inspected GPU execution path.
Pre-merge checks: Passed 4

Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title "gpu: keep GPU tick timestamps for Mario Kart World" matches the main change. The PR adds a title-specific check, UsesLegacyAmpereTimestamps, so Mario Kart World keeps the legacy GPU tick ti…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Title check

Explanation

The title "gpu: keep GPU tick timestamps for Mario Kart World" matches the main change. The PR adds a title-specific check, UsesLegacyAmpereTimestamps, so Mario Kart World keeps the legacy GPU tick timestamps. The gpu.cpp change applies this check in GetTicks, and the test covers the new function and the tick conversion. The title is short, specific, and follows the repository's area-prefix style. It does not name the Switch 2 scope or the dynamic-resolution motivation, but the PR does not need to cover every detail.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 7fb7f3a2-2dd8-4042-bdc3-199a0704579f
Commits

Reviewing files that changed from the base of the PR and between 28edf9f and 9387b52.

Files selected for processing: 3

  • src/tests/video_core/gpu_timestamp.cpp
  • src/video_core/gpu.cpp
  • src/video_core/gpu_timestamp.h

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

@Octelly

Octelly commented Nov 14, 2026

Copy link
Copy Markdown
Contributor

Performance win on Linux with RX 6600 (8GB VRAM)

Nx2NoteListHostWrite queued a new list copy of a page when a write
couldn't be patched into the existing copy. Nx2CopyListPages copies the
host buffer, which doesn't hold CPU-written words until the buffer is
next synchronized, so the new copy was marked current with the GPU's
older data. DmaPusher then parsed that data as commands: Mario Kart World
(RX 6600, RADV) read float words as "method 0x1 count 8063", ran garbage
puller methods and render targets, and aborted after 8-21 minutes.

Leave the copy stale instead, as before Eglantine-Community#794: Nx2ReadListCopy answers
Unknown and the list is read the safe way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YQZoXmd1tepstZpngXBzCj
@rdenno

rdenno commented Nov 14, 2026

Copy link
Copy Markdown
Collaborator Author

Added aeab4fc for the crash @Octelly hit with this PR (RX 6600, RADV). Both logs abort the same way: the command reader parses float data (0x3F7F0001, logged as "method 0x1 count 8063") at the start of a command list that is valid in memory, then runs garbage until an assert.

The cause is from #794, not the timestamp change. When a CPU write can't be patched into a command list page's copy, Nx2NoteListHostWrite queues a new copy from the host buffer. That buffer doesn't have the CPU-written words yet, so the new copy is marked current with the GPU's older data. The commit goes back to the pre-#794 behaviour of leaving the copy stale, so the list is read the safe way.

MKW performance is unchanged (31.1 fps in the same test). I can't reproduce the crash on my machine (it took 8–21 minutes on Linux), so it needs a longer session on an affected setup to confirm.

🤖 Generated with Claude Code

@rdenno rdenno added the priority: high High priority label Nov 14, 2026
@rdenno rdenno moved this from Backlog to In progress in Release Tracker Nov 14, 2026
@rdenno rdenno moved this from In progress to Ready in Release Tracker Nov 14, 2026
@rdenno
rdenno merged commit 2ecab36 into Eglantine-Community:nx2-experimental Nov 14, 2026
15 checks passed
@github-project-automation github-project-automation Bot moved this from Ready to Done in Release Tracker Nov 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: high High priority

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants