fix(@projects/@magic-civilization): 🐛 handle Weston/llvmpipe framebuffer staleness
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
710f08d961
commit
155e7ea5f4
3 changed files with 69 additions and 34 deletions
|
|
@ -133,10 +133,18 @@ func _ready() -> void:
|
|||
)
|
||||
return
|
||||
elif _scene == "replay_viewer_proof":
|
||||
# Do NOT use change_scene_to_file — under Weston/llvmpipe the composited
|
||||
# framebuffer is not flushed after a scene swap, causing get_texture to
|
||||
# return the stale main-menu frame. Instantiate directly as a child of
|
||||
# root so no scene transition occurs and the proof scene owns its own
|
||||
# capture + quit.
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
get_tree().change_scene_to_file(
|
||||
"res://engine/scenes/tests/proof_replay_viewer.tscn"
|
||||
)
|
||||
var packed: PackedScene = load("res://engine/scenes/tests/proof_replay_viewer.tscn")
|
||||
if packed == null:
|
||||
push_error("ScreenCapture: cannot load proof_replay_viewer.tscn")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
get_tree().root.add_child(packed.instantiate())
|
||||
return
|
||||
elif _scene == "world_map":
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
extends Control
|
||||
## Proof scene for p2-46 bullet 8 — headless replay-viewer exercise.
|
||||
##
|
||||
## Entered via capture_screenshot.gd (add_child path, no change_scene_to_file)
|
||||
## so the framebuffer is never staled by a scene swap under Weston/llvmpipe.
|
||||
##
|
||||
## 1. Writes a synthetic 50-turn fixture via GdReplayArchive.write_fixture.
|
||||
## 2. Instantiates the replay_viewer scene and wires it to the fixture UUID.
|
||||
## 3. Drives goto_turn(25) → 3 s hold → goto_turn(50) → 3 s hold → screenshot.
|
||||
## 3. Drives _goto_turn(25) → 3 s hold → screenshot (p2-46-replay-viewer-T25).
|
||||
## 4. Drives _goto_turn(50) → 3 s hold → screenshot (p2-46-replay-viewer-T50).
|
||||
## 5. Quits.
|
||||
##
|
||||
## Scene key: "replay_viewer_proof" (capture_screenshot.gd dispatcher).
|
||||
## Run via: tools/screenshot.sh proof_replay_viewer replay_viewer_proof
|
||||
## Run via: tools/screenshot.sh p2-46-replay-viewer-T25 replay_viewer_proof
|
||||
|
||||
const OUTPUT_DIR: String = "user://screenshots"
|
||||
const REPLAY_VIEWER_SCENE: String = "res://engine/scenes/menus/replay_viewer.tscn"
|
||||
|
|
@ -16,7 +21,6 @@ var _viewer_scene: Node = null
|
|||
var _player: GdReplayPlayer = null
|
||||
var _uuid: String = ""
|
||||
var _archive_root: String = ""
|
||||
var _captured: bool = false
|
||||
|
||||
var _turn_label: Label = null
|
||||
var _status_label: Label = null
|
||||
|
|
@ -26,7 +30,7 @@ func _ready() -> void:
|
|||
get_viewport().size = Vector2i(1920, 1080)
|
||||
DisplayServer.window_set_size(Vector2i(1920, 1080))
|
||||
|
||||
# Dark background panel so the viewer is visible in the screenshot.
|
||||
# Opaque dark background — covers any scene rendered underneath us.
|
||||
var bg: ColorRect = ColorRect.new()
|
||||
bg.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
bg.color = Color(0.04, 0.04, 0.08, 1.0)
|
||||
|
|
@ -46,6 +50,9 @@ func _ready() -> void:
|
|||
_turn_label.text = ""
|
||||
add_child(_turn_label)
|
||||
|
||||
# Yield several frames so our ColorRect draws over any prior scene content.
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
|
||||
_archive_root = ProjectSettings.globalize_path("user://archive")
|
||||
|
|
@ -54,52 +61,60 @@ func _ready() -> void:
|
|||
_uuid = _archive.write_fixture(_archive_root, "age-of-dwarves", "Cycle-X Proof")
|
||||
if _uuid.is_empty():
|
||||
push_error("proof_replay_viewer: write_fixture failed")
|
||||
_capture_and_quit()
|
||||
_capture("p2-46-replay-viewer-T25-error")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
|
||||
_status_label.text = "p2-46 Replay Viewer Proof — fixture UUID: %s" % _uuid
|
||||
|
||||
# Load the replay viewer scene as a sub-scene.
|
||||
var packed: PackedScene = load(REPLAY_VIEWER_SCENE)
|
||||
if packed == null:
|
||||
push_error("proof_replay_viewer: cannot load replay_viewer.tscn")
|
||||
_capture_and_quit()
|
||||
_capture("p2-46-replay-viewer-T25-error")
|
||||
get_tree().quit(1)
|
||||
return
|
||||
|
||||
_viewer_scene = packed.instantiate()
|
||||
# Set game_id before _ready fires so the viewer loads history automatically.
|
||||
_viewer_scene.set("game_id", _uuid)
|
||||
_viewer_scene.set("pack", "age-of-dwarves")
|
||||
|
||||
# Anchor the sub-scene below the status labels.
|
||||
if _viewer_scene is Control:
|
||||
_viewer_scene.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
_viewer_scene.set_anchor(SIDE_TOP, 0.09)
|
||||
add_child(_viewer_scene)
|
||||
|
||||
# Allow _ready on the viewer to complete (history loaded, scrubber configured).
|
||||
# Allow viewer _ready to complete (history loaded, scrubber configured).
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
|
||||
# Obtain the player separately to drive goto_turn for the status overlay.
|
||||
# Separate player for status overlay — viewer owns its own internally.
|
||||
_player = GdReplayPlayer.new()
|
||||
var loaded: bool = _player.load_history(_archive_root, "age-of-dwarves", _uuid)
|
||||
if not loaded:
|
||||
push_error("proof_replay_viewer: GdReplayPlayer.load_history failed for %s" % _uuid)
|
||||
|
||||
# Drive to turn 25 and hold for screenshot opportunity.
|
||||
# Turn 25 — drive, hold 3 s, capture.
|
||||
_drive_to_turn(25)
|
||||
await get_tree().create_timer(3.0).timeout
|
||||
# Multiple process frames before capture: frame_post_draw does not fire
|
||||
# under Weston/llvmpipe, so we pump the render loop manually.
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
_capture("p2-46-replay-viewer-T25")
|
||||
|
||||
# Drive to turn 50 and hold.
|
||||
# Turn 50 — drive, hold 3 s, capture, quit.
|
||||
_drive_to_turn(50)
|
||||
await get_tree().create_timer(3.0).timeout
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
await get_tree().process_frame
|
||||
_capture("p2-46-replay-viewer-T50")
|
||||
|
||||
_capture_and_quit()
|
||||
get_tree().quit()
|
||||
|
||||
|
||||
func _drive_to_turn(t: int) -> void:
|
||||
## Push the scrubber on the embedded viewer and update our overlay.
|
||||
if _viewer_scene != null and _viewer_scene.has_method("_goto_turn"):
|
||||
_viewer_scene._goto_turn(t)
|
||||
|
||||
|
|
@ -117,26 +132,17 @@ func _drive_to_turn(t: int) -> void:
|
|||
)
|
||||
|
||||
|
||||
func _capture_and_quit() -> void:
|
||||
if _captured:
|
||||
return
|
||||
_captured = true
|
||||
|
||||
func _capture(name: String) -> void:
|
||||
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(OUTPUT_DIR))
|
||||
|
||||
var image: Image = get_viewport().get_texture().get_image()
|
||||
if image == null:
|
||||
push_error("proof_replay_viewer: viewport image unavailable")
|
||||
get_tree().quit(1)
|
||||
push_error("proof_replay_viewer: viewport image unavailable for %s" % name)
|
||||
return
|
||||
|
||||
var timestamp: String = (
|
||||
Time.get_datetime_string_from_system()
|
||||
.replace(":", "-")
|
||||
.replace("T", "_")
|
||||
var abs_path: String = ProjectSettings.globalize_path(
|
||||
"%s/%s.png" % [OUTPUT_DIR, name]
|
||||
)
|
||||
var rel_path: String = "%s/proof_replay_viewer_%s.png" % [OUTPUT_DIR, timestamp]
|
||||
var abs_path: String = ProjectSettings.globalize_path(rel_path)
|
||||
var err: Error = image.save_png(abs_path)
|
||||
|
||||
if err == OK:
|
||||
|
|
@ -145,6 +151,4 @@ func _capture_and_quit() -> void:
|
|||
image.get_width(), image.get_height(), abs_path
|
||||
])
|
||||
else:
|
||||
push_error("proof_replay_viewer: save failed: %s" % error_string(err))
|
||||
|
||||
get_tree().quit()
|
||||
push_error("proof_replay_viewer: save failed (%s): %s" % [name, error_string(err)])
|
||||
|
|
|
|||
|
|
@ -35,6 +35,29 @@ timeout 60 flatpak run --user \
|
|||
--rendering-method gl_compatibility \
|
||||
--fixed-fps 10 2>&1 | tee /tmp/godot_screenshot_log.txt || true
|
||||
|
||||
# replay_viewer_proof writes multiple named files (no timestamp suffix).
|
||||
# Collect them separately from the standard single-file path.
|
||||
if [ "$SCENE" = "replay_viewer_proof" ]; then
|
||||
mapfile -t PROOF_PATHS < <(ls -t "$GODOT_USERDATA"/p2-46-replay-viewer-*.png 2>/dev/null)
|
||||
if [ "${#PROOF_PATHS[@]}" -eq 0 ]; then
|
||||
echo "ERROR: No p2-46-replay-viewer-*.png found in $GODOT_USERDATA"
|
||||
grep -E "Screenshot|ERROR|SCRIPT" /tmp/godot_screenshot_log.txt | head -10
|
||||
exit 1
|
||||
fi
|
||||
if ssh -o ConnectTimeout=3 plum "echo ok" >/dev/null 2>&1; then
|
||||
for p in "${PROOF_PATHS[@]}"; do
|
||||
BASENAME="$(basename "$p")"
|
||||
scp "$p" "$PLUM_DESKTOP/${BASENAME}" && \
|
||||
echo "Sent to plum: ~/Desktop/${BASENAME}"
|
||||
done
|
||||
else
|
||||
echo "WARNING: plum not reachable, screenshots at:"
|
||||
for p in "${PROOF_PATHS[@]}"; do echo " $p"; done
|
||||
fi
|
||||
echo "=== Done ==="
|
||||
exit 0
|
||||
fi
|
||||
|
||||
SCREENSHOT_PATH=$(ls -t "$GODOT_USERDATA"/${NAME}_*.png 2>/dev/null | head -1)
|
||||
|
||||
if [ -z "$SCREENSHOT_PATH" ] || [ ! -f "$SCREENSHOT_PATH" ]; then
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue