Upload tile delta to GPU#4159
Conversation
WalkthroughThis PR refactors the render pass enable system by splitting a shared ChangesRender pass routing and scatter-based tile uploads
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/client/render/gl/passes/TileScatterPass.ts (1)
53-62: ⚡ Quick winAdd FBO completeness check after attaching texture.
If
tileTexhas an incompatible format or is incomplete, the FBO will silently fail. A quick check helps catch shader/texture setup bugs early.🛡️ Suggested FBO completeness check
gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tileTex, 0, ); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) !== gl.FRAMEBUFFER_COMPLETE) { + console.error("TileScatterPass: FBO incomplete"); + } gl.bindFramebuffer(gl.FRAMEBUFFER, null);🤖 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/TileScatterPass.ts` around lines 53 - 62, After attaching tileTex to the framebuffer in TileScatterPass (this.fbo), call gl.checkFramebufferStatus(gl.FRAMEBUFFER) and verify it equals gl.FRAMEBUFFER_COMPLETE; if not, log or throw a clear error including the returned status so texture/format incompatibilities are detected early. Add this check right after the framebufferTexture2D call and before unbinding the framebuffer (use the this.fbo and tileTex symbols to locate the code), and ensure the code cleans up or fails fast when the status is not COMPLETE.
🤖 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.
Inline comments:
In `@src/client/render/gl/passes/TileScatterPass.ts`:
- Around line 114-127: The flush() method in TileScatterPass sets
gl.viewport(0,0,this.mapW,this.mapH) for the FBO but never restores the previous
viewport, causing subsequent draws (e.g. drawBaseLayer()) to render with the
wrong viewport; before calling gl.viewport save the current viewport (via
gl.getParameter(gl.VIEWPORT) or equivalent), perform the FBO draw as currently
implemented (bindFramebuffer, viewport, drawArrays, unbind), then restore the
saved viewport with gl.viewport(...) after gl.bindFramebuffer(gl.FRAMEBUFFER,
null) so the canvas rendering state is unchanged; update the method handling in
TileScatterPass.flush() (and related fbo/viewport logic) to use this
save/restore sequence.
---
Nitpick comments:
In `@src/client/render/gl/passes/TileScatterPass.ts`:
- Around line 53-62: After attaching tileTex to the framebuffer in
TileScatterPass (this.fbo), call gl.checkFramebufferStatus(gl.FRAMEBUFFER) and
verify it equals gl.FRAMEBUFFER_COMPLETE; if not, log or throw a clear error
including the returned status so texture/format incompatibilities are detected
early. Add this check right after the framebufferTexture2D call and before
unbinding the framebuffer (use the this.fbo and tileTex symbols to locate the
code), and ensure the code cleans up or fails fast when the status is not
COMPLETE.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 608fc816-26fc-4ff6-af61-f65f5abdabc4
⛔ Files ignored due to path filters (2)
src/client/render/gl/shaders/map-overlay/tile-scatter.frag.glslis excluded by!**/*.glslsrc/client/render/gl/shaders/map-overlay/tile-scatter.vert.glslis excluded by!**/*.glsl
📒 Files selected for processing (6)
src/client/render/gl/RenderSettings.tssrc/client/render/gl/Renderer.tssrc/client/render/gl/debug/Layout.tssrc/client/render/gl/passes/TerritoryPass.tssrc/client/render/gl/passes/TileScatterPass.tssrc/client/render/gl/render-settings.json
| gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); | ||
| gl.viewport(0, 0, this.mapW, this.mapH); | ||
| gl.disable(gl.BLEND); | ||
|
|
||
| gl.useProgram(this.program); | ||
| gl.uniform2f(this.uMapSize, this.mapW, this.mapH); | ||
|
|
||
| gl.bindVertexArray(this.vao); | ||
| gl.drawArrays(gl.POINTS, 0, this.patchCount); | ||
|
|
||
| gl.bindFramebuffer(gl.FRAMEBUFFER, null); | ||
|
|
||
| this.patchCount = 0; | ||
| } |
There was a problem hiding this comment.
Viewport is not restored after FBO draw.
flush() sets viewport to (0, 0, mapW, mapH) for the tile texture FBO, but doesn't restore it. Per Renderer.ts, flushTileTexture() runs during the texture upload phase before drawBaseLayer(). If the canvas size differs from the map size, subsequent draws will use the wrong viewport.
🐛 Save and restore viewport
gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo);
+ const oldViewport = gl.getParameter(gl.VIEWPORT);
gl.viewport(0, 0, this.mapW, this.mapH);
gl.disable(gl.BLEND);
gl.useProgram(this.program);
gl.uniform2f(this.uMapSize, this.mapW, this.mapH);
gl.bindVertexArray(this.vao);
gl.drawArrays(gl.POINTS, 0, this.patchCount);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ gl.viewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
this.patchCount = 0;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); | |
| gl.viewport(0, 0, this.mapW, this.mapH); | |
| gl.disable(gl.BLEND); | |
| gl.useProgram(this.program); | |
| gl.uniform2f(this.uMapSize, this.mapW, this.mapH); | |
| gl.bindVertexArray(this.vao); | |
| gl.drawArrays(gl.POINTS, 0, this.patchCount); | |
| gl.bindFramebuffer(gl.FRAMEBUFFER, null); | |
| this.patchCount = 0; | |
| } | |
| gl.bindFramebuffer(gl.FRAMEBUFFER, this.fbo); | |
| const oldViewport = gl.getParameter(gl.VIEWPORT); | |
| gl.viewport(0, 0, this.mapW, this.mapH); | |
| gl.disable(gl.BLEND); | |
| gl.useProgram(this.program); | |
| gl.uniform2f(this.uMapSize, this.mapW, this.mapH); | |
| gl.bindVertexArray(this.vao); | |
| gl.drawArrays(gl.POINTS, 0, this.patchCount); | |
| gl.bindFramebuffer(gl.FRAMEBUFFER, null); | |
| gl.viewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]); | |
| this.patchCount = 0; |
🤖 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/TileScatterPass.ts` around lines 114 - 127, The
flush() method in TileScatterPass sets gl.viewport(0,0,this.mapW,this.mapH) for
the FBO but never restores the previous viewport, causing subsequent draws (e.g.
drawBaseLayer()) to render with the wrong viewport; before calling gl.viewport
save the current viewport (via gl.getParameter(gl.VIEWPORT) or equivalent),
perform the FBO draw as currently implemented (bindFramebuffer, viewport,
drawArrays, unbind), then restore the saved viewport with gl.viewport(...) after
gl.bindFramebuffer(gl.FRAMEBUFFER, null) so the canvas rendering state is
unchanged; update the method handling in TileScatterPass.flush() (and related
fbo/viewport logic) to use this save/restore sequence.
Description
Reduces the amount of tile data sent to the gpu each tick, roughly ~10fps rate increase on 10 year old chromebook.
Two changes to the territory rendering path:
1. Split
passEnabled.mapOverlayinto four flagsThe single
mapOverlaytoggle controlled four unrelated passes (territory fill, border compute, border stamp, trail). Splits it intoterritory,borderCompute,borderStamp,trailso each can be toggled independently in the debug GUI. Pure rename — default behavior is unchanged (all four default totrue).2. GPU scatter for per-frame tile texture updates
Replaces the dirty-row bbox
texSubImage2Dupload inTerritoryPasswith a newTileScatterPassthat uploads a small attribute buffer of(x, y, state)patches and runs a singlePOINTSdraw into an FBO bound totileTex. Each patch rasterizes as a 1×1 point into exactly its target texel.Why: the old path's cost scaled with the bounding box of the dirty rows, not the number of changed tiles. In typical play, tile changes are spread across the whole map (multiple players fighting in different regions, scattered trails/fallout), so the bbox covered most of the map's rows and we re-uploaded mostly-unchanged data every frame. The new path is constant cost in patch count regardless of spatial distribution, and no longer scales with map size.
The full-upload path (initial load / seek / spawn-phase flush) is unchanged.
fullUploadPendingcorrectly supersedes any queued scatter patches.Please complete the following:
Please put your Discord username so you can be contacted if a bug or regression is found:
evan