Add tests for scenery surface caching and preview behavior
- Introduced `test_scenery_brick_preview.gd` to validate focused placement and CPU draw-cost for large brick walls. - Added `test_scenery_surface_cache.gd` to assess texture budget, visual equivalence, and cache invalidation. - Created `test_scenery_surface_preview.gd` to evaluate adaptive preview selection and placement behavior. - Implemented various helper functions to measure drawing performance and validate expected behaviors during tests. - Added unique identifiers for new test files to ensure proper tracking and management.
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user