spaceship/tests/test_guidance_marker_redraw.gd
Loïc Wisniewski 7d1a584c15 Stop redrawing what only needs moving
Reviewed against the rule issues #6 and #10 set for the buildings: geometry
is rasterised once and thereafter only moved, scaled or faded, because every
NeonDraw call expands into four antialiased polylines.

The planet visuals never got that refactor. The black hole rebuilt ten
glowing paths of twenty-five points every frame - forty polylines - to spin
five rings; each ring is a NeonPiece that rotates now. The research lab spent
nine draw calls a frame, eight of them fixed geometry, to turn one dish and
blink six lights; the dish is a child that rotates and the lights a child
whose modulate breathes. The gas giant's bands genuinely deform, so they
still draw, but its constant rim moved out to a child. NeonPiece gained
add_path, which it needed for open arcs.

And I had broken the rule myself. ScannerGuidance re-queued a redraw of its
arrow every frame for a colour that changes only with the destination - and
it runs with PROCESS_MODE_ALWAYS, so it was re-tessellating through every
dialog that stops the rest of the game. Guarded, and pinned by a test that
counts CanvasItem's own draw signal: 36 rasterisations in 0.6s before, one
after.

Two smaller things from the same pass: the station menu drew its condition
bar by hand where the ship lab already uses a ProgressBar node, so it uses
one now and its _draw is gone; and station/upkeep refreshes were wired into
_refresh_garrison(), which nothing but a test calls, so both menus showed
whatever was true when they opened.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
2026-09-08 11:35:45 +02:00

111 lines
3.6 KiB
GDScript

extends SceneTree
# The flight marker must not re-rasterise itself to point somewhere.
#
# Issues #6 and #10 set this rule for the rest of the game: geometry is drawn
# ONCE and thereafter moved, rotated, scaled or faded, because every NeonDraw
# call expands into four antialiased polylines and redrawing to animate
# re-tessellates all of them. The marker broke it - it re-queued a redraw
# every frame for a glow colour that only changes when the DESTINATION does -
# and it is the worst node in the game to get wrong, because it runs with
# PROCESS_MODE_ALWAYS: it kept working through every dialog that stops the
# rest of the game.
#
# Counted off CanvasItem's own `draw` signal rather than a counter of ours,
# so what is measured is real rasterisation.
#
# Covers:
# A. a marker tracking one destination settles - it stops redrawing
# B. it is genuinely still tracking (so this cannot pass by freezing)
# C. a destination of a different colour does redraw it, once
var state: Node
var scene: Node
var indicator: Control
var draws: int = 0
var stage: int = 0
var elapsed: float = 0.0
var settled_at: int = 0
var first_position: Vector2
func _initialize() -> void:
state = root.get_node("GameState")
state.reset_new_game()
state.intro_active = false
state.reset_quests()
state.buildings.clear()
scene = load("res://world/Main.tscn").instantiate()
root.add_child(scene)
current_scene = scene
indicator = scene.get_node("UI/ScannerGuidance/Indicator")
indicator.draw.connect(func(): draws += 1)
func _process(delta: float) -> bool:
elapsed += delta
match stage:
0:
if elapsed < 0.4:
return false
# A destination far off, so the marker is pinned to a screen edge
# and its position keeps being recomputed every frame.
state.scanner_target = {
"planet_id": "redraw:probe", "definition_id": "rocky",
"name": "Probe", "world_position": Vector2(90000.0, -40000.0),
}
elapsed = 0.0
stage = 1
return false
1:
# Let it take up the target and draw whatever it needs to.
if elapsed < 0.5:
return false
settled_at = draws
first_position = indicator.position
elapsed = 0.0
stage = 2
return false
2:
# --- A. thirty-odd frames of tracking, no rasterising ---------
if elapsed < 0.6:
return false
var while_tracking: int = draws - settled_at
if while_tracking > 1:
return _fail("the marker rasterised %d times in %.1fs of tracking one destination" \
% [while_tracking, elapsed])
# --- B. and it really was tracking ---------------------------
if not scene.get_node("UI/ScannerGuidance").visible:
return _fail("the marker was not even visible, so this proves nothing")
print("tracking one destination: %d redraws in %.1fs" % [while_tracking, elapsed])
# --- C. a different world does repaint it --------------------
var before: int = draws
state.scanner_target = {
"planet_id": "redraw:probe2", "definition_id": "gas_giant",
"name": "Probe Two", "world_position": Vector2(-70000.0, 55000.0),
}
elapsed = 0.0
stage = 3
settled_at = before
return false
3:
if elapsed < 0.4:
return false
var repaints: int = draws - settled_at
if repaints < 1:
return _fail("changing destination never recoloured the marker")
if repaints > 2:
return _fail("changing destination rasterised %d times, expected about one" % repaints)
print("changing destination repaints it %d time(s), then settles" % repaints)
print("TEST_GUIDANCE_MARKER_REDRAW: PASS")
quit(0)
return true
return false
func _fail(message: String) -> bool:
print("TEST_GUIDANCE_MARKER_REDRAW: FAIL (%s)" % message)
quit(1)
return true