Skip to content

fix(client): fallback to full border recompute on massive tile changes#4576

Merged
evanpelle merged 1 commit into
openfrontio:mainfrom
blontd6:fix/nuke-detonation-border-lag
Jul 16, 2026
Merged

fix(client): fallback to full border recompute on massive tile changes#4576
evanpelle merged 1 commit into
openfrontio:mainfrom
blontd6:fix/nuke-detonation-border-lag

Conversation

@blontd6

@blontd6 blontd6 commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Add approved & assigned issue number here:

Resolves #3604
(related)

Description:

Fixes severe client-side lag spikes (framerate dropping to single digits) when multiple nuclear bombs detonate close to each other with "isWaterNukes": true enabled.

  • Water nukes convert land to water on detonation. For an Atom Bomb (radius 30), this changes up to 2,800 tiles.

  • The client splits these changes across 16 frames (dripBuckets), processing ~150 tile changes per frame.

  • For each tile change, BorderComputePass.patchTile computes updates. If the victim's territory is highlighted, the pass expands the repaint area of each tile change to a highlightThicken Chebyshev box of radius 4 (meaning 9x9 = 81 points).

  • This forces the CPU to calculate and push up to 150 * 81 = 12,150 points per frame to the border scatter buffer, uploading it, and rendering it as thousands of overlapping GPU POINTS using the complex border shader.

  • Under consecutive detonations, this completely freezes the client rendering thread, leading to disconnections.

  • Optimizes BorderComputePass.ts to fallback to a full-screen quad border recompute if the scatter point count exceeds a threshold of 2,048 points.

  • Bypasses the expensive CPU coordinate expansions and array resizing for subsequent tile updates in the same frame.

  • The GPU renders a simple fullscreen quad to rebuild all borders, which is extremely parallelized and executes in <0.1ms, keeping gameplay smooth.

Please complete the following:

  • I have added screenshots for all UI updates (N/A)
  • I process any text displayed to the user through translateText() and I've added it to the en.json file (N/A)
  • I have added relevant tests to the test directory (N/A)

Please put your Discord username so you can be contacted if a bug or regression is found:

blontd6

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

BorderComputePass.patchTile now avoids incremental updates during full recomputes and falls back to a full recompute when the scatter queue exceeds 2048 items.

Changes

Border compute safeguards

Layer / File(s) Summary
Scatter queue safeguards
src/client/render/gl/passes/BorderComputePass.ts
patchTile returns when globalDirty is set and marks a full recompute while clearing scatter when its count exceeds 2048.

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

Possibly related PRs

Poem

Scatter queues grow, then clear the way,
Full recomputes step in to stay.
Borders refresh with steady light,
Neighbors wait for a cleaner flight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The patch only adds a border-update fallback and does not deliver the waterNukes terrain, minimap, navigation, and packet-sync work described in #3604. If this is meant to close #3604, include the waterNukes feature work or retarget this PR to the follow-up performance fix.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: falling back to a full border recompute on large tile-update bursts.
Description check ✅ Passed The description matches the change and stays on-topic, describing the lag fix and border recompute fallback.
Out of Scope Changes check ✅ Passed The only code change is a border recompute safeguard, which stays aligned with the performance goal and adds no unrelated behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the small-fix Small fix (≤ 50 lines) — auto-applied by PR gate label Jul 12, 2026

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

🧹 Nitpick comments (1)
src/client/render/gl/passes/BorderComputePass.ts (1)

200-200: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting the 2048 threshold into a named constant.

A named constant such as SCATTER_FULL_RECOMPUTE_THRESHOLD would make the intent self-documenting and simplify future tuning.

♻️ Optional refactor
+private static readonly SCATTER_FULL_RECOMPUTE_THRESHOLD = 2048;
+
   patchTile(x: number, y: number, prevOwner: number, newOwner: number): void {
     if (this.globalDirty) return;
-    if (this.scatter.count > 2048) {
+    if (this.scatter.count > BorderComputePass.SCATTER_FULL_RECOMPUTE_THRESHOLD) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/render/gl/passes/BorderComputePass.ts` at line 200, Extract the
literal 2048 used in the condition within BorderComputePass into a named
constant such as SCATTER_FULL_RECOMPUTE_THRESHOLD, then use that constant in
place of the literal while preserving the existing comparison behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/client/render/gl/passes/BorderComputePass.ts`:
- Line 200: Extract the literal 2048 used in the condition within
BorderComputePass into a named constant such as
SCATTER_FULL_RECOMPUTE_THRESHOLD, then use that constant in place of the literal
while preserving the existing comparison behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1858e443-5805-4391-938c-0857d989cb18

📥 Commits

Reviewing files that changed from the base of the PR and between d766913 and bb184fa.

📒 Files selected for processing (1)
  • src/client/render/gl/passes/BorderComputePass.ts

@evanpelle evanpelle left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

thanks!

@github-project-automation github-project-automation Bot moved this from Triage to Final Review in OpenFront Release Management Jul 16, 2026
@evanpelle evanpelle merged commit 46162c0 into openfrontio:main Jul 16, 2026
11 of 12 checks passed
@github-project-automation github-project-automation Bot moved this from Final Review to Complete in OpenFront Release Management Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

small-fix Small fix (≤ 50 lines) — auto-applied by PR gate

Projects

Status: Complete

Development

Successfully merging this pull request may close these issues.

2 participants