Files
open-wc/src/tools/verify_m2_build_batch_planner.gd
T

218 lines
6.1 KiB
GDScript

extends SceneTree
## Asset-free contract, dependency and timing regression for M2 batch planning.
const PLANNER_SCRIPT := preload("res://src/render/m2/m2_build_batch_planner.gd")
const PLANNER_PATH := "res://src/render/m2/m2_build_batch_planner.gd"
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
func _initialize() -> void:
var failures: Array[String] = []
_verify_static_batch_limit(failures)
_verify_animated_batch_limit(failures)
_verify_non_positive_limit_clamp(failures)
_verify_remaining_count_cap(failures)
_verify_exact_group_completion(failures)
_verify_empty_and_past_end(failures)
_verify_historical_negative_offset(failures)
_verify_source_boundaries(failures)
var elapsed_milliseconds := _verify_bounded_timing(failures)
if not failures.is_empty():
for failure in failures:
push_error("M2_BUILD_BATCH_PLANNER: %s" % failure)
quit(1)
return
print(
"M2_BUILD_BATCH_PLANNER PASS cases=8 iterations=20000 elapsed_ms=%.3f"
% elapsed_milliseconds
)
quit(0)
func _verify_static_batch_limit(failures: Array[String]) -> void:
var plan := _plan(300, 0, false, 8, 128)
_expect_plan(plan, 128, 128, 128, false, "static batch", failures)
func _verify_animated_batch_limit(failures: Array[String]) -> void:
var plan := _plan(300, 0, true, 8, 128)
_expect_plan(plan, 8, 8, 8, false, "animated batch", failures)
func _verify_non_positive_limit_clamp(failures: Array[String]) -> void:
var animated_plan := _plan(4, 0, true, 0, 128)
_expect_plan(animated_plan, 1, 1, 1, false, "zero animated limit", failures)
var static_plan := _plan(4, 0, false, 8, -10)
_expect_plan(static_plan, 1, 1, 1, false, "negative static limit", failures)
func _verify_remaining_count_cap(failures: Array[String]) -> void:
var plan := _plan(140, 128, false, 8, 128)
_expect_plan(plan, 128, 12, 0, true, "remaining cap", failures)
func _verify_exact_group_completion(failures: Array[String]) -> void:
var plan := _plan(16, 8, true, 8, 128)
_expect_plan(plan, 8, 8, 0, true, "exact completion", failures)
func _verify_empty_and_past_end(failures: Array[String]) -> void:
var empty_plan := _plan(0, 0, false, 8, 128)
_expect_plan(empty_plan, 128, 0, 0, true, "empty group", failures)
var past_end_plan := _plan(10, 11, false, 8, 128)
_expect_plan(past_end_plan, 128, 0, 0, true, "past end", failures)
func _verify_historical_negative_offset(failures: Array[String]) -> void:
var plan := _plan(10, -2, false, 8, 4)
_expect_plan(plan, 4, 4, 2, false, "negative offset", failures)
func _verify_source_boundaries(failures: Array[String]) -> void:
var loader_source := _read_text(LOADER_PATH, failures)
var planner_source := _read_text(PLANNER_PATH, failures)
var build_loop_source := _source_between(
loader_source,
"func _process_m2_build_jobs()",
"func _materialize_m2_group_batch(",
failures
)
_expect_equal(
build_loop_source.count("_m2_build_batch_planner.plan_batch("),
1,
"one loader planner adapter",
failures
)
_expect_true(
not build_loop_source.contains("var batch_size:"),
"loader omits batch-size formula",
failures
)
_expect_true(
not build_loop_source.contains("mini(batch_size"),
"loader omits batch-count formula",
failures
)
for forbidden_dependency in [
"WorkerThreadPool",
"RenderingServer",
"ResourceLoader",
"MultiMesh",
"Node",
"Mutex",
]:
_expect_true(
not planner_source.contains(forbidden_dependency),
"planner omits %s" % forbidden_dependency,
failures
)
func _verify_bounded_timing(failures: Array[String]) -> float:
var planner := PLANNER_SCRIPT.new()
var started_microseconds := Time.get_ticks_usec()
for iteration in range(20000):
var plan: Dictionary = planner.plan_batch(
512,
iteration % 512,
iteration % 3 == 0,
8,
128
)
if int(plan.get("batch_count", -1)) < 0:
failures.append("timing iteration returned negative batch count")
break
var elapsed_milliseconds := float(Time.get_ticks_usec() - started_microseconds) / 1000.0
_expect_true(elapsed_milliseconds < 1000.0, "20000 batch plans bounded", failures)
return elapsed_milliseconds
func _plan(
transform_count: int,
current_offset: int,
has_animated_prototype: bool,
animated_batch_limit: int,
static_batch_limit: int
) -> Dictionary:
var planner := PLANNER_SCRIPT.new()
return planner.plan_batch(
transform_count,
current_offset,
has_animated_prototype,
animated_batch_limit,
static_batch_limit
)
func _expect_plan(
plan: Dictionary,
expected_batch_size: int,
expected_batch_count: int,
expected_next_offset: int,
expected_group_complete: bool,
label: String,
failures: Array[String]
) -> void:
_expect_equal(
int(plan.get("effective_batch_size", -1)),
expected_batch_size,
"%s effective batch size" % label,
failures
)
_expect_equal(
int(plan.get("batch_count", -1)),
expected_batch_count,
"%s batch count" % label,
failures
)
_expect_equal(
int(plan.get("next_offset", -1)),
expected_next_offset,
"%s next offset" % label,
failures
)
_expect_true(
bool(plan.get("group_complete", not expected_group_complete))
== expected_group_complete,
"%s group complete" % label,
failures
)
func _read_text(path: String, failures: Array[String]) -> String:
var file := FileAccess.open(path, FileAccess.READ)
if file == null:
failures.append("cannot open %s" % path)
return ""
return file.get_as_text()
func _source_between(
source: String,
start_marker: String,
end_marker: String,
failures: Array[String]
) -> String:
var start_offset := source.find(start_marker)
var end_offset := source.find(end_marker, start_offset + start_marker.length())
if start_offset < 0 or end_offset <= start_offset:
failures.append("cannot isolate source between %s and %s" % [start_marker, end_marker])
return ""
return source.substr(start_offset, end_offset - start_offset)
func _expect_equal(
actual_value: int,
expected_value: int,
label: String,
failures: Array[String]
) -> void:
if actual_value != expected_value:
failures.append("%s expected %d, got %d" % [label, expected_value, actual_value])
func _expect_true(actual_value: bool, label: String, failures: Array[String]) -> void:
if not actual_value:
failures.append("%s expected true" % label)