fix(world-map): 🐛 Fix camera texture refresh to prevent stale rendering in offscreen matches by updating occlusion handling

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Claude Code 2026-04-11 06:37:53 -07:00
parent 921a68a7d9
commit 28e9994e39

View file

@ -110,7 +110,10 @@ func _disable_blocking_overlays() -> void:
func _apply_arena_camera_fit() -> void:
## Find the background camera and zoom out so the whole map fits the
## arena cell viewport. Called deferred so it runs after world_map's
## own _start_game completes its camera setup.
## own _start_game completes its camera setup. Also forces the map
## SubViewport's update_mode to UPDATE_ALWAYS so Godot doesn't skip
## its draw pass when the arena window is occluded by another arena
## window — without this the offscreen matches show stale textures.
var game_map: RefCounted = GameState.get_game_map()
if game_map == null:
return
@ -120,6 +123,13 @@ func _apply_arena_camera_fit() -> void:
var cam: Camera2D = vwm.get_background_camera()
if cam == null or not cam.has_method("fit_map_to_viewport"):
return
var bg_vp: SubViewport = null
if vwm.has_method("get_background_viewport"):
bg_vp = vwm.call("get_background_viewport") as SubViewport
if bg_vp != null:
bg_vp.render_target_update_mode = SubViewport.UPDATE_ALWAYS
var viewport: Viewport = _world_map.get_viewport()
if viewport == null:
return
@ -222,20 +232,25 @@ func _on_turn_ended(_turn_number: int, _player_index: int) -> void:
func _capture_viewport_screenshot_async() -> void:
## Godot on Wayland skips rendering occluded/unfocused windows, so a
## raw `get_viewport().get_texture().get_image()` here grabs whatever
## stale frame was last drawn — which for windows stacked behind
## other arena instances is the loading-screen splash. Wait a few
## idle frames AND force an explicit draw pass before grabbing, so
## every window renders at least one real gameplay frame to its
## texture before we read it back.
## Capture the root viewport's texture after forcing a draw pass.
## Godot on Wayland skips rendering occluded windows, so we set
## update_mode = UPDATE_ALWAYS on the map SubViewport (once, when the
## arena helper comes up) and, at capture time, yield a few frames
## and call RenderingServer.force_draw() to flush the pipeline. The
## world map renders into a SubViewport owned by ViewportWindowManager;
## SubViewportContainer composites it into the root viewport each frame,
## so grabbing the root viewport gives us HUD + map in one image.
## (Grabbing the SubViewport directly returned 2x2 because its size is
## tied to the Control's layout state, which isn't deterministic at
## capture time.)
if _result_dir.is_empty():
return
var tree: SceneTree = _world_map.get_tree()
for _i: int in range(3):
for _i: int in range(4):
await tree.process_frame
RenderingServer.force_draw()
await tree.process_frame
await tree.process_frame
var viewport: Viewport = _world_map.get_viewport()
if viewport == null:
@ -252,7 +267,12 @@ func _capture_viewport_screenshot_async() -> void:
if err != OK:
push_warning("[AI ARENA] screenshot save failed: %s" % error_string(err))
return
print("[AI ARENA] screenshot saved: %s" % path)
print(
(
"[AI ARENA] screenshot saved: %s (%dx%d)"
% [path, image.get_width(), image.get_height()]
)
)
func _on_victory(player_index: int, victory_type: String) -> void: