Skip to content

USB: Follow the PCRTC video mode for GunCon2 aim - #14770

Open
dyzt wants to merge 2 commits into
PCSX2:masterfrom
dyzt:guncon2-follow-video-mode
Open

USB: Follow the PCRTC video mode for GunCon2 aim#14770
dyzt wants to merge 2 commits into
PCSX2:masterfrom
dyzt:guncon2-follow-video-mode

Conversation

@dyzt

@dyzt dyzt commented Jul 27, 2026

Copy link
Copy Markdown

Description of Changes

The per-serial calibration table is applied once by AutoConfigure() and never revisited, but its values are video mode dependent -- the table carries commented-out 480i/480p twins for three discs to prove it.

Time Crisis 2 and 3 halve the vertical mode when entering split-screen co-op (640x448 -> 640x224). The range of positions the gun can report halves with it while the frozen scale keeps sending the full range, so aim travels twice as far as it should.

Scale the vertical deflection by the ratio between the live PCRTC resolution and the one latched on first use. The ratio is exactly 1.0 for a game that never changes mode, so output is unchanged everywhere else, and the correction is only armed when a table row actually matched -- manually configured guns and unlisted games are untouched.

Only the vertical is corrected. The horizontal reading also moves when one of the two display circuits is disabled (Time Crisis 3 reports 320x224 for several frames while transitioning), which is a change in what is being composited rather than in video timing.

The ratio is clamped so it can only reduce the deflection. The reference is not saved to the state, so a state made in split-screen latches the reduced height on load; the clamp keeps that inert rather than letting it overshoot by 2x when the game returns to full height.

Rationale behind Changes

Fixes split-screen aim scaling with Guncon2 in Time Crisis 2 and Time Crisis 3

Suggested Testing Steps

  • Launch Time Crisis 2 or Time Crisis 3 in split-screen
  • Check log for scaling ratio
  • Quit to main menu, aim returns to normal
  • Start single player, aim returns to normal
  • Launch non-TC2/TC3 game (I tested with Vampire Night), scaling ratio remains at 1.0 and aim is correct.

Did you use AI to help find, test, or implement this issue or feature?

Claude Opus 5 used to implement, fix suggested by stenzek in a thread about the issue. Tested manually.

The per-serial calibration table is applied once by AutoConfigure() and
never revisited, but its values are video mode dependent -- the table
carries commented-out 480i/480p twins for three discs to prove it.

Time Crisis 2 and 3 halve the vertical mode when entering split-screen
co-op (640x448 -> 640x224). The range of positions the gun can report
halves with it while the frozen scale keeps sending the full range, so
aim travels twice as far as it should.

Scale the vertical deflection by the ratio between the live PCRTC
resolution and the one latched on first use. The ratio is exactly 1.0
for a game that never changes mode, so output is unchanged everywhere
else, and the correction is only armed when a table row actually
matched -- manually configured guns and unlisted games are untouched.

Only the vertical is corrected. The horizontal reading also moves when
one of the two display circuits is disabled (Time Crisis 3 reports
320x224 for several frames while transitioning), which is a change in
what is being composited rather than in video timing.

The ratio is clamped so it can only reduce the deflection. The
reference is not saved to the state, so a state made in split-screen
latches the reduced height on load; the clamp keeps that inert rather
than letting it overshoot by 2x when the game returns to full height.

@github-actions github-actions Bot 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.

Thank you for submitting a contribution to PCSX2

As this is your first pull request, please be aware of the contributing guidelines.

Additionally, as per recent changes in GitHub Actions, your pull request will need to be approved by a maintainer before GitHub Actions can run against it. You can find more information about this change here.

Please be patient until this happens. In the meantime if you'd like to confirm the builds are passing, you have the option of opening a PR on your own fork, just make sure your fork's master branch is up to date!

@dyzt
dyzt marked this pull request as ready for review July 27, 2026 09:29
@refractionpcsx2 refractionpcsx2 added the LLM Codegen PR contains LLM generated code label Jul 27, 2026
@Mrlinkwii Mrlinkwii added this to the Release 2.X milestone Jul 27, 2026

@chaoticgd chaoticgd 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.

Haven't tested it, but from a brief look at the code:

I don't think you can access g_gs_renderer from the CPU thread like that. For example, if the GS thread decides to reopen when the CPU thread is in GSgetDisplayResolution the local gs variable will become a dangling pointer.

@dyzt
dyzt force-pushed the guncon2-follow-video-mode branch from 93cae5d to 0894e7e Compare July 28, 2026 07:28
@dyzt
dyzt requested a review from chaoticgd July 28, 2026 07:38

@chaoticgd chaoticgd 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.

I can't properly test this since I don't have the perhipheral in question, so I'll just dimiss my stale review rather than reviewing again.

Technically those variables should be synchronized e.g. with an std::atomic (if the type passed is large it makes a lock). On x86 cache coherency isn't an issue at least but you are lying to the compiler which can lead to odd behaviour. Granted the existing code doesn't do it.

@chaoticgd
chaoticgd dismissed their stale review July 28, 2026 12:04

Issue fixed, can't test the PR.

GSgetDisplayResolution() took a raw pointer from g_gs_renderer and
dereferenced it, but guncon2 calls it from the EE thread. If the GS
thread reopened the renderer between the null check and the read, the
pointer dangled.

Store the resolution on the GS thread where it is already computed and
read the copy instead. This matches s_last_draw_rect directly above it,
which exists for this exact reason and which the same guncon2 call site
already reads via GSTranslateWindowToDisplayCoordinates.

The snapshot is a relaxed atomic so that the compiler knows it is
shared. x86 cache coherency was never the problem, but a plain static
would leave it free to cache the value in a register, hoist the load, or
split the two components either side of a mode change. Keeping it to one
atomic rather than two means width and height always come from the same
frame, and the static_assert rejects a non-lock-free fallback.

Reported by chaoticgd.
@dyzt
dyzt force-pushed the guncon2-follow-video-mode branch from 0894e7e to 3c670e9 Compare July 28, 2026 17:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants