perf: bake platformer level tiles into chunked images + merge colliders - #212
Merged
Conversation
Investigates examples/platformer perf: the level's static ground was one Image + one RectangleCollider per tile (233 tiles), costing a per-frame SceneObject in the renderer's depth sort and a collider in the compositor's collision checks for every tile regardless of motion. - BGE.TileMap.bakeTileMapImages()/mergeTileColliderRuns(): new generic engine helpers that bake a sparse tile grid into a handful of composited bitmaps (one per occupied chunk, sized/anchored to each chunk's actual tile bounding box - never clips a tile whose visual offset pushes it past a grid-aligned bucket boundary) and merge same-tag collider runs within a row, respectively. - examples/platformer/Level.bs rewritten to use them: colliders 244->24, scene objects 257->34 on the same level. - Engine fix: SceneObject.isPotentiallyOnScreen() now checks a screen- aligned object's full bounding box (via getBoundingPoints()) instead of a single anchor point, so a large baked chunk (or any large Image/Rectangle/Circle/Text) isn't wholesale-culled just because its own anchor corner left the frustum while another corner is still on screen - regression test in SceneObjectImage.spec.bs. - Game.debugPrintPerfStats(): new opt-in (off by default, zero cost when off), console-only per-frame stats - entity/collider counts and phase timing (UI input / update+collision / entity draw / swap) - independent of and faster-cadence than the existing GC print. This is what surfaced the above findings. - chunkSize capped at 128 in Level.bs: binary search on both the BrightScript Simulator and a real device found >=192 silently fails to render a screen-aligned Image at all, despite provably-correct content (GetByteArray + tmp:/ PNG round-trip) and the engine's own draw path reporting success - a real, unexplained platform limit, not a bug in this baking logic. Filed as issue #211 (multi-frame room loading) for the constructive follow-up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Investigates
examples/platformerperformance: the level's static ground was oneImage+ oneRectangleColliderper tile (233 tiles), costing a per-frameSceneObjectin the renderer's depth sort and a collider in the compositor's collision checks for every tile regardless of whether it ever moves.BGE.TileMap.bakeTileMapImages()/mergeTileColliderRuns()(new, generic engine namespace,src/source/utils/tilemap/): bakes a sparse tile grid into a handful of composited bitmaps (one per occupied chunk, sized/anchored to that chunk's actual tile bounding box — so a tile whose visual offset pushes it past a grid-aligned bucket boundary is never clipped) and merges same-tag collider runs within a row.examples/platformer/Level.bsrewritten to use them: colliders 244→24, scene objects 257→34, on the same level layout.SceneObject.isPotentiallyOnScreen()now checks a screen-aligned object's full bounding box (viagetBoundingPoints()) instead of collapsing to a single anchor point, so a large baked chunk (or any largeImage/Rectangle/Circle/Text) isn't wholesale-culled just because its own anchor corner left the frustum while another corner is still on screen. Regression test inSceneObjectImage.spec.bs.Game.debugPrintPerfStats(): new opt-in (off by default, zero cost when off) console-only per-frame stats — entity/collider counts and phase timing (UI input / update+collision / entity draw / swap) — independent of, and a faster cadence than, the existing GC print. This is the tool that surfaced everything else in this PR.chunkSizecapped at 128 inLevel.bs: binary search on both the BrightScript Simulator and a real device found that>=192silently fails to render a screen-alignedImageat all, despite provably-correct content (confirmed viaGetByteArrayand atmp:/PNG round-trip) and the engine's own draw path reporting success — a real, unexplained platform limit, not a bug in this baking logic. Filed as Support multi-frame loading for Room setup (spread heavy onCreate work like tile baking across frames) #211 (multi-frame room loading) for the constructive follow-up rather than chasing the exact native cause further.Testing
npm run check(lint + validate + headless Rooibos suite): 1105 passed.debugPrintPerfStats().Related
🤖 Generated with Claude Code