extends SceneTree ## Focused placement regression and CPU draw-cost comparison for large brick walls. const WALL := preload("res://scripts/stage/scenery/stage_wall_panel.gd") const CONTROLLER := preload("res://scripts/editors/stage/stage_scenery_controller.gd") class TimedWall extends StageWallPanel: var draw_times_usec: Array[int] = [] func _draw() -> void: var started: int = Time.get_ticks_usec() super._draw() draw_times_usec.append(Time.get_ticks_usec() - started) var _failures: Array[String] = [] func _initialize() -> void: call_deferred("_run") func _run() -> void: root.size = Vector2i(2560, 1440) var world := Node2D.new() var ghosts := Node2D.new() var controller := CONTROLLER.new() as StageSceneryController root.add_child(world) root.add_child(ghosts) root.add_child(controller) controller.configure(world, ghosts, null) controller.set_grid_snap(true, 15.0) var tint := Color("#995544") controller.begin_wall(&"finish_wall_brick", Vector2(-300.0, -150.0), tint) var ghost: StageWallPanel = ghosts.get_child(0) as StageWallPanel _expect(ghost.interactive_preview, "placement uses the inexpensive brick preview") var press := InputEventMouseButton.new() press.button_index = MOUSE_BUTTON_LEFT press.pressed = true controller.handle_mouse_button(press, Vector2(-300.0, -150.0), false) controller.handle_mouse_motion(Vector2(2100.0, 1150.0)) var preview_bounds: Rect2 = ghost.global_transform * ghost.get_local_bounds() var release := InputEventMouseButton.new() release.button_index = MOUSE_BUTTON_LEFT controller.handle_mouse_button(release, Vector2(2100.0, 1150.0), false) _expect(world.get_child_count() == 1, "release commits one large wall") var committed: StageWallPanel = world.get_child(0) as StageWallPanel _expect(not committed.interactive_preview, "committed wall restores the full brick artwork") _expect(committed.finish_id == &"finish_wall_brick" and committed.tint == tint, "commit preserves the chosen finish and tint") _expect(preview_bounds.is_equal_approx(committed.global_transform * committed.get_local_bounds()), "preview and commit use the same snapped bounds across negative world coordinates") controller.exit_tool() world.free() ghosts.free() controller.free() var sample := TimedWall.new() sample.position = Vector2(1280.0, 720.0) sample.interactive_preview = true sample.configure(Vector2(2400.0, 1320.0), &"finish_wall_brick", tint) root.add_child(sample) await process_frame await process_frame var draw_count: int = sample.draw_times_usec.size() _expect(draw_count > 0, "large wall executes its drawing callback") sample.configure(sample.panel_size, sample.finish_id, sample.tint) await process_frame await process_frame _expect(sample.draw_times_usec.size() == draw_count, "unchanged snapped geometry does not rebuild the drawing") var preview_usec: float = await _measure_redraws(sample) sample.interactive_preview = false await process_frame await process_frame var detailed_usec: float = await _measure_redraws(sample) print("Brick draw CPU time (2400 x 1320): preview=%.2f ms, detailed=%.2f ms, speedup=%.1fx" % [preview_usec / 1000.0, detailed_usec / 1000.0, detailed_usec / maxf(1.0, preview_usec)]) sample.free() if _failures.is_empty(): print("PASS: scenery brick placement preview") else: for failure: String in _failures: push_error("FAIL: " + failure) quit(0 if _failures.is_empty() else 1) func _measure_redraws(sample: TimedWall) -> float: sample.draw_times_usec.clear() for _iteration: int in 5: sample.queue_redraw() await process_frame await process_frame var total_usec: float = 0.0 for elapsed: int in sample.draw_times_usec: total_usec += float(elapsed) return total_usec / maxf(1.0, float(sample.draw_times_usec.size())) func _expect(condition: bool, message: String) -> void: if not condition: _failures.append(message)