render: add M2 build resource snapshot
This commit is contained in:
@@ -14,20 +14,19 @@ const ACTION_ADVANCE_WITHOUT_MATERIALIZATION := &"advance_without_materializatio
|
||||
## Returns a detached action/transition plan for one M2 build operation.
|
||||
func plan_step(
|
||||
batch_count: int,
|
||||
animation_request_pending: bool,
|
||||
has_animated_prototype: bool,
|
||||
has_static_mesh: bool,
|
||||
static_model_missing: bool
|
||||
resource_snapshot: RefCounted
|
||||
) -> Dictionary:
|
||||
if animation_request_pending:
|
||||
if resource_snapshot == null:
|
||||
return _wait_plan(ACTION_WAIT_FOR_STATIC_MESH)
|
||||
if bool(resource_snapshot.call("animation_request_pending")):
|
||||
return _wait_plan(ACTION_WAIT_FOR_ANIMATION)
|
||||
if batch_count <= 0:
|
||||
return _advance_plan(ACTION_ADVANCE_WITHOUT_MATERIALIZATION, false)
|
||||
if has_animated_prototype:
|
||||
if bool(resource_snapshot.call("has_animated_prototype")):
|
||||
return _advance_plan(ACTION_MATERIALIZE_ANIMATED, true)
|
||||
if has_static_mesh:
|
||||
if bool(resource_snapshot.call("has_static_mesh")):
|
||||
return _advance_plan(ACTION_MATERIALIZE_STATIC, true)
|
||||
if static_model_missing:
|
||||
if bool(resource_snapshot.call("static_model_missing")):
|
||||
return _advance_plan(ACTION_ADVANCE_WITHOUT_MATERIALIZATION, true)
|
||||
return _wait_plan(ACTION_WAIT_FOR_STATIC_MESH)
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
class_name M2BuildResourceSnapshot
|
||||
extends RefCounted
|
||||
|
||||
## Holds one M2 build-step resource observation without owning engine lifetime.
|
||||
|
||||
var _normalized_relative_path: String
|
||||
var _animated_prototype: Node3D
|
||||
var _animation_request_pending: bool
|
||||
var _static_mesh: Mesh = null
|
||||
var _static_model_missing: bool = false
|
||||
|
||||
|
||||
func _init(
|
||||
normalized_relative_path: String,
|
||||
animated_prototype: Node3D,
|
||||
animation_request_pending: bool
|
||||
) -> void:
|
||||
_normalized_relative_path = normalized_relative_path
|
||||
_animated_prototype = animated_prototype
|
||||
_animation_request_pending = animation_request_pending
|
||||
|
||||
|
||||
## Returns the normalized M2 path observed by the loader.
|
||||
func normalized_relative_path() -> String:
|
||||
return _normalized_relative_path
|
||||
|
||||
|
||||
## Returns the borrowed animated prototype without transferring ownership.
|
||||
func animated_prototype() -> Node3D:
|
||||
return _animated_prototype
|
||||
|
||||
|
||||
## Returns whether an animated prototype was observed.
|
||||
func has_animated_prototype() -> bool:
|
||||
return _animated_prototype != null
|
||||
|
||||
|
||||
## Returns whether the animation request remains pending.
|
||||
func animation_request_pending() -> bool:
|
||||
return _animation_request_pending
|
||||
|
||||
|
||||
## Adopts the optional static Mesh and terminal missing-model observation.
|
||||
func adopt_static_observation(static_mesh: Mesh, static_model_missing: bool) -> void:
|
||||
_static_mesh = static_mesh
|
||||
_static_model_missing = static_model_missing
|
||||
|
||||
|
||||
## Returns the borrowed static Mesh without transferring ownership.
|
||||
func static_mesh() -> Mesh:
|
||||
return _static_mesh
|
||||
|
||||
|
||||
## Returns whether a prepared static Mesh was observed.
|
||||
func has_static_mesh() -> bool:
|
||||
return _static_mesh != null
|
||||
|
||||
|
||||
## Returns whether the static model lookup reached a terminal missing outcome.
|
||||
func static_model_missing() -> bool:
|
||||
return _static_model_missing
|
||||
|
||||
|
||||
## Returns detached path/availability diagnostics without engine references.
|
||||
func diagnostic_snapshot() -> Dictionary:
|
||||
return {
|
||||
"normalized_relative_path": _normalized_relative_path,
|
||||
"has_animated_prototype": has_animated_prototype(),
|
||||
"animation_request_pending": _animation_request_pending,
|
||||
"has_static_mesh": has_static_mesh(),
|
||||
"static_model_missing": _static_model_missing,
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://cgleoy15rtmby
|
||||
@@ -63,6 +63,9 @@ const M2_BUILD_BATCH_PLANNER_SCRIPT := preload(
|
||||
const M2_BUILD_DISPATCH_PLANNER_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_build_dispatch_planner.gd"
|
||||
)
|
||||
const M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_build_resource_snapshot.gd"
|
||||
)
|
||||
const M2_BUILD_QUEUE_SCRIPT := preload("res://src/render/m2/m2_build_queue.gd")
|
||||
const M2_STATIC_BATCH_MATERIALIZER_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_static_batch_materializer.gd"
|
||||
@@ -4350,31 +4353,38 @@ func _process_m2_build_jobs() -> void:
|
||||
enable_m2_animated_instances
|
||||
and _m2_animation_load_pipeline_state.has_request(normalized_rel)
|
||||
)
|
||||
var resource_snapshot: RefCounted = M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT.new(
|
||||
normalized_rel,
|
||||
animated_prototype,
|
||||
animation_request_pending
|
||||
)
|
||||
var batch_plan: Dictionary = {}
|
||||
var batch_count := 0
|
||||
var static_mesh: Mesh = null
|
||||
var static_model_missing := false
|
||||
if not animation_request_pending:
|
||||
if not bool(resource_snapshot.call("animation_request_pending")):
|
||||
batch_plan = _m2_build_batch_planner.plan_batch(
|
||||
transforms.size(),
|
||||
offset,
|
||||
animated_prototype != null,
|
||||
bool(resource_snapshot.call("has_animated_prototype")),
|
||||
m2_animated_instances_per_tick,
|
||||
m2_multimesh_batch_size
|
||||
)
|
||||
batch_count = int(batch_plan["batch_count"])
|
||||
if batch_count > 0 and animated_prototype == null:
|
||||
static_mesh = _get_m2_mesh_or_request(rel_path)
|
||||
static_model_missing = (
|
||||
static_mesh == null
|
||||
and _m2_prototype_cache_state.is_model_missing(normalized_rel)
|
||||
if (
|
||||
batch_count > 0
|
||||
and not bool(resource_snapshot.call("has_animated_prototype"))
|
||||
):
|
||||
var static_mesh := _get_m2_mesh_or_request(rel_path)
|
||||
resource_snapshot.call(
|
||||
"adopt_static_observation",
|
||||
static_mesh,
|
||||
(
|
||||
static_mesh == null
|
||||
and _m2_prototype_cache_state.is_model_missing(normalized_rel)
|
||||
)
|
||||
)
|
||||
var dispatch_plan: Dictionary = _m2_build_dispatch_planner.plan_step(
|
||||
batch_count,
|
||||
animation_request_pending,
|
||||
animated_prototype != null,
|
||||
static_mesh != null,
|
||||
static_model_missing
|
||||
resource_snapshot
|
||||
)
|
||||
if bool(dispatch_plan["rotate_queue"]):
|
||||
_m2_build_queue_state.rotate_front()
|
||||
@@ -4392,7 +4402,7 @@ func _process_m2_build_jobs() -> void:
|
||||
_materialize_m2_animated_batch(
|
||||
root as Node3D,
|
||||
rel_path,
|
||||
animated_prototype,
|
||||
resource_snapshot.call("animated_prototype") as Node3D,
|
||||
transforms,
|
||||
offset,
|
||||
batch_count,
|
||||
@@ -4405,7 +4415,7 @@ func _process_m2_build_jobs() -> void:
|
||||
_materialize_m2_group_batch(
|
||||
root as Node3D,
|
||||
rel_path,
|
||||
static_mesh,
|
||||
resource_snapshot.call("static_mesh") as Mesh,
|
||||
transforms,
|
||||
offset,
|
||||
batch_count,
|
||||
@@ -4431,8 +4441,6 @@ func _process_m2_build_jobs() -> void:
|
||||
if next_group_index >= group_keys.size():
|
||||
_finish_m2_build_job(key)
|
||||
_m2_build_queue_state.pop_front()
|
||||
|
||||
|
||||
func _materialize_m2_group_batch(
|
||||
m2_root: Node3D,
|
||||
rel_path: String,
|
||||
|
||||
@@ -6,6 +6,9 @@ extends SceneTree
|
||||
const PLANNER_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_build_dispatch_planner.gd"
|
||||
)
|
||||
const SNAPSHOT_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_build_resource_snapshot.gd"
|
||||
)
|
||||
const PLANNER_PATH := "res://src/render/m2/m2_build_dispatch_planner.gd"
|
||||
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
|
||||
|
||||
@@ -32,8 +35,10 @@ func _initialize() -> void:
|
||||
|
||||
func _verify_wait_and_empty_actions(failures: Array[String]) -> void:
|
||||
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||
var prototype := Node3D.new()
|
||||
var snapshot := _make_snapshot(prototype, true, ArrayMesh.new(), true)
|
||||
var animation_wait: Dictionary = planner.call(
|
||||
"plan_step", 4, true, true, true, true
|
||||
"plan_step", 4, snapshot
|
||||
)
|
||||
_expect_plan(
|
||||
animation_wait,
|
||||
@@ -44,7 +49,7 @@ func _verify_wait_and_empty_actions(failures: Array[String]) -> void:
|
||||
failures
|
||||
)
|
||||
var empty_batch: Dictionary = planner.call(
|
||||
"plan_step", 0, false, true, true, true
|
||||
"plan_step", 0, _make_snapshot(prototype, false, ArrayMesh.new(), true)
|
||||
)
|
||||
_expect_plan(
|
||||
empty_batch,
|
||||
@@ -55,7 +60,7 @@ func _verify_wait_and_empty_actions(failures: Array[String]) -> void:
|
||||
failures
|
||||
)
|
||||
var negative_batch: Dictionary = planner.call(
|
||||
"plan_step", -3, false, false, false, false
|
||||
"plan_step", -3, _make_snapshot(null, false, null, false)
|
||||
)
|
||||
_expect_plan(
|
||||
negative_batch,
|
||||
@@ -65,12 +70,14 @@ func _verify_wait_and_empty_actions(failures: Array[String]) -> void:
|
||||
"negative batch advances without serial",
|
||||
failures
|
||||
)
|
||||
prototype.free()
|
||||
|
||||
|
||||
func _verify_materialization_priority(failures: Array[String]) -> void:
|
||||
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||
var prototype := Node3D.new()
|
||||
var animated: Dictionary = planner.call(
|
||||
"plan_step", 3, false, true, true, true
|
||||
"plan_step", 3, _make_snapshot(prototype, false, ArrayMesh.new(), true)
|
||||
)
|
||||
_expect_plan(
|
||||
animated,
|
||||
@@ -81,7 +88,7 @@ func _verify_materialization_priority(failures: Array[String]) -> void:
|
||||
failures
|
||||
)
|
||||
var static_mesh: Dictionary = planner.call(
|
||||
"plan_step", 3, false, false, true, true
|
||||
"plan_step", 3, _make_snapshot(null, false, ArrayMesh.new(), true)
|
||||
)
|
||||
_expect_plan(
|
||||
static_mesh,
|
||||
@@ -91,12 +98,13 @@ func _verify_materialization_priority(failures: Array[String]) -> void:
|
||||
"prepared static mesh wins over missing flag",
|
||||
failures
|
||||
)
|
||||
prototype.free()
|
||||
|
||||
|
||||
func _verify_static_retry_and_missing_actions(failures: Array[String]) -> void:
|
||||
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||
var static_wait: Dictionary = planner.call(
|
||||
"plan_step", 2, false, false, false, false
|
||||
"plan_step", 2, _make_snapshot(null, false, null, false)
|
||||
)
|
||||
_expect_plan(
|
||||
static_wait,
|
||||
@@ -107,7 +115,7 @@ func _verify_static_retry_and_missing_actions(failures: Array[String]) -> void:
|
||||
failures
|
||||
)
|
||||
var missing: Dictionary = planner.call(
|
||||
"plan_step", 2, false, false, false, true
|
||||
"plan_step", 2, _make_snapshot(null, false, null, true)
|
||||
)
|
||||
_expect_plan(
|
||||
missing,
|
||||
@@ -121,10 +129,12 @@ func _verify_static_retry_and_missing_actions(failures: Array[String]) -> void:
|
||||
|
||||
func _verify_fresh_results(failures: Array[String]) -> void:
|
||||
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||
var first: Dictionary = planner.call("plan_step", 1, false, true, false, false)
|
||||
var prototype := Node3D.new()
|
||||
var snapshot := _make_snapshot(prototype, false, null, false)
|
||||
var first: Dictionary = planner.call("plan_step", 1, snapshot)
|
||||
first["action"] = &"mutated"
|
||||
first["rotate_queue"] = true
|
||||
var second: Dictionary = planner.call("plan_step", 1, false, true, false, false)
|
||||
var second: Dictionary = planner.call("plan_step", 1, snapshot)
|
||||
_expect_plan(
|
||||
second,
|
||||
PLANNER_SCRIPT.ACTION_MATERIALIZE_ANIMATED,
|
||||
@@ -133,6 +143,7 @@ func _verify_fresh_results(failures: Array[String]) -> void:
|
||||
"plan results are detached",
|
||||
failures
|
||||
)
|
||||
prototype.free()
|
||||
|
||||
|
||||
func _verify_loader_and_dependency_boundaries(failures: Array[String]) -> void:
|
||||
@@ -176,15 +187,18 @@ func _verify_loader_and_dependency_boundaries(failures: Array[String]) -> void:
|
||||
|
||||
func _verify_bounded_timing(failures: Array[String]) -> float:
|
||||
var planner: RefCounted = PLANNER_SCRIPT.new()
|
||||
var snapshots := [
|
||||
_make_snapshot(null, true, null, false),
|
||||
_make_snapshot(null, false, null, false),
|
||||
_make_snapshot(null, false, ArrayMesh.new(), false),
|
||||
_make_snapshot(null, false, null, true),
|
||||
]
|
||||
var started_microseconds := Time.get_ticks_usec()
|
||||
for iteration in range(20000):
|
||||
planner.call(
|
||||
"plan_step",
|
||||
iteration % 5,
|
||||
iteration % 11 == 0,
|
||||
iteration % 3 == 0,
|
||||
iteration % 3 == 1,
|
||||
iteration % 7 == 0
|
||||
snapshots[iteration % snapshots.size()]
|
||||
)
|
||||
var elapsed_milliseconds := (
|
||||
float(Time.get_ticks_usec() - started_microseconds) / 1000.0
|
||||
@@ -197,6 +211,25 @@ func _verify_bounded_timing(failures: Array[String]) -> float:
|
||||
return elapsed_milliseconds
|
||||
|
||||
|
||||
func _make_snapshot(
|
||||
animated_prototype: Node3D,
|
||||
animation_request_pending: bool,
|
||||
static_mesh: Mesh,
|
||||
static_model_missing: bool
|
||||
) -> RefCounted:
|
||||
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new(
|
||||
"world/test.m2",
|
||||
animated_prototype,
|
||||
animation_request_pending
|
||||
)
|
||||
snapshot.call(
|
||||
"adopt_static_observation",
|
||||
static_mesh,
|
||||
static_model_missing
|
||||
)
|
||||
return snapshot
|
||||
|
||||
|
||||
func _expect_plan(
|
||||
plan: Dictionary,
|
||||
expected_action: StringName,
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
extends SceneTree
|
||||
|
||||
## Asset-free M2 resource observation identity, adoption, lifetime, boundary and
|
||||
## bounded-timing regression.
|
||||
|
||||
const SNAPSHOT_SCRIPT := preload(
|
||||
"res://src/render/m2/m2_build_resource_snapshot.gd"
|
||||
)
|
||||
const SNAPSHOT_PATH := "res://src/render/m2/m2_build_resource_snapshot.gd"
|
||||
const DISPATCH_PATH := "res://src/render/m2/m2_build_dispatch_planner.gd"
|
||||
const LOADER_PATH := "res://src/scenes/streaming/streaming_world_loader.gd"
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
var failures: Array[String] = []
|
||||
_verify_initial_observation(failures)
|
||||
_verify_static_adoption_and_replacement(failures)
|
||||
_verify_detached_diagnostics(failures)
|
||||
_verify_engine_lifetime(failures)
|
||||
_verify_loader_and_dependency_boundaries(failures)
|
||||
var elapsed_milliseconds := _verify_bounded_timing(failures)
|
||||
if not failures.is_empty():
|
||||
for failure in failures:
|
||||
push_error("M2_BUILD_RESOURCE_SNAPSHOT: %s" % failure)
|
||||
quit(1)
|
||||
return
|
||||
print(
|
||||
"M2_BUILD_RESOURCE_SNAPSHOT PASS cases=12 iterations=20000 elapsed_ms=%.3f"
|
||||
% elapsed_milliseconds
|
||||
)
|
||||
quit(0)
|
||||
|
||||
|
||||
func _verify_initial_observation(failures: Array[String]) -> void:
|
||||
var prototype := Node3D.new()
|
||||
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new(
|
||||
"world/example.m2",
|
||||
prototype,
|
||||
true
|
||||
)
|
||||
_expect_string(
|
||||
String(snapshot.call("normalized_relative_path")),
|
||||
"world/example.m2",
|
||||
"normalized path retained",
|
||||
failures
|
||||
)
|
||||
_expect_same(
|
||||
snapshot.call("animated_prototype"),
|
||||
prototype,
|
||||
"animated prototype identity retained",
|
||||
failures
|
||||
)
|
||||
_expect_true(
|
||||
bool(snapshot.call("has_animated_prototype")),
|
||||
"animated availability",
|
||||
failures
|
||||
)
|
||||
_expect_true(
|
||||
bool(snapshot.call("animation_request_pending")),
|
||||
"animation pending retained",
|
||||
failures
|
||||
)
|
||||
_expect_false(bool(snapshot.call("has_static_mesh")), "static defaults empty", failures)
|
||||
_expect_false(
|
||||
bool(snapshot.call("static_model_missing")),
|
||||
"missing defaults false",
|
||||
failures
|
||||
)
|
||||
prototype.free()
|
||||
|
||||
|
||||
func _verify_static_adoption_and_replacement(failures: Array[String]) -> void:
|
||||
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new("world/static.m2", null, false)
|
||||
var first_mesh := ArrayMesh.new()
|
||||
var second_mesh := ArrayMesh.new()
|
||||
snapshot.call("adopt_static_observation", first_mesh, true)
|
||||
_expect_same(snapshot.call("static_mesh"), first_mesh, "first Mesh identity", failures)
|
||||
_expect_true(bool(snapshot.call("has_static_mesh")), "static available", failures)
|
||||
_expect_true(
|
||||
bool(snapshot.call("static_model_missing")),
|
||||
"contradictory missing flag retained",
|
||||
failures
|
||||
)
|
||||
snapshot.call("adopt_static_observation", second_mesh, false)
|
||||
_expect_same(
|
||||
snapshot.call("static_mesh"),
|
||||
second_mesh,
|
||||
"replacement Mesh identity",
|
||||
failures
|
||||
)
|
||||
_expect_false(
|
||||
bool(snapshot.call("static_model_missing")),
|
||||
"replacement missing flag",
|
||||
failures
|
||||
)
|
||||
snapshot.call("adopt_static_observation", null, true)
|
||||
_expect_false(bool(snapshot.call("has_static_mesh")), "Mesh may clear", failures)
|
||||
_expect_true(bool(snapshot.call("static_model_missing")), "terminal missing adopted", failures)
|
||||
|
||||
|
||||
func _verify_detached_diagnostics(failures: Array[String]) -> void:
|
||||
var prototype := Node3D.new()
|
||||
var mesh := ArrayMesh.new()
|
||||
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new("world/diag.m2", prototype, false)
|
||||
snapshot.call("adopt_static_observation", mesh, true)
|
||||
var diagnostics: Dictionary = snapshot.call("diagnostic_snapshot")
|
||||
_expect_true(
|
||||
diagnostics == {
|
||||
"normalized_relative_path": "world/diag.m2",
|
||||
"has_animated_prototype": true,
|
||||
"animation_request_pending": false,
|
||||
"has_static_mesh": true,
|
||||
"static_model_missing": true,
|
||||
},
|
||||
"diagnostic scalar contract",
|
||||
failures
|
||||
)
|
||||
_expect_false(diagnostics.values().has(prototype), "diagnostics omit prototype", failures)
|
||||
_expect_false(diagnostics.values().has(mesh), "diagnostics omit Mesh", failures)
|
||||
diagnostics["has_static_mesh"] = false
|
||||
var fresh: Dictionary = snapshot.call("diagnostic_snapshot")
|
||||
_expect_true(bool(fresh["has_static_mesh"]), "diagnostics detached", failures)
|
||||
prototype.free()
|
||||
|
||||
|
||||
func _verify_engine_lifetime(failures: Array[String]) -> void:
|
||||
var prototype := Node3D.new()
|
||||
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new("world/lifetime.m2", prototype, false)
|
||||
snapshot = null
|
||||
_expect_true(is_instance_valid(prototype), "snapshot release keeps Node valid", failures)
|
||||
prototype.free()
|
||||
|
||||
|
||||
func _verify_loader_and_dependency_boundaries(failures: Array[String]) -> void:
|
||||
var loader_source := FileAccess.get_file_as_string(LOADER_PATH)
|
||||
var dispatch_source := FileAccess.get_file_as_string(DISPATCH_PATH)
|
||||
var snapshot_source := FileAccess.get_file_as_string(SNAPSHOT_PATH)
|
||||
for delegated_token in [
|
||||
"M2_BUILD_RESOURCE_SNAPSHOT_SCRIPT.new(",
|
||||
"\"adopt_static_observation\"",
|
||||
"resource_snapshot.call(\"animated_prototype\")",
|
||||
"resource_snapshot.call(\"static_mesh\")",
|
||||
]:
|
||||
_expect_true(
|
||||
loader_source.contains(delegated_token),
|
||||
"loader uses %s" % delegated_token,
|
||||
failures
|
||||
)
|
||||
_expect_true(
|
||||
dispatch_source.contains("resource_snapshot.call(\"animation_request_pending\")")
|
||||
and dispatch_source.contains("resource_snapshot.call(\"has_static_mesh\")"),
|
||||
"dispatch planner consumes typed snapshot",
|
||||
failures
|
||||
)
|
||||
for forbidden_token in [
|
||||
"queue_free",
|
||||
"free(",
|
||||
"ResourceLoader",
|
||||
"RenderingServer",
|
||||
"FileAccess",
|
||||
"WorkerThreadPool",
|
||||
"Thread",
|
||||
"Mutex",
|
||||
]:
|
||||
_expect_false(
|
||||
snapshot_source.contains(forbidden_token),
|
||||
"snapshot omits %s ownership" % forbidden_token,
|
||||
failures
|
||||
)
|
||||
|
||||
|
||||
func _verify_bounded_timing(failures: Array[String]) -> float:
|
||||
var mesh := ArrayMesh.new()
|
||||
var started_microseconds := Time.get_ticks_usec()
|
||||
for iteration in range(20000):
|
||||
var snapshot: RefCounted = SNAPSHOT_SCRIPT.new(
|
||||
"world/%d.m2" % (iteration % 64),
|
||||
null,
|
||||
iteration % 5 == 0
|
||||
)
|
||||
snapshot.call(
|
||||
"adopt_static_observation",
|
||||
mesh if iteration % 3 == 0 else null,
|
||||
iteration % 7 == 0
|
||||
)
|
||||
snapshot.call("diagnostic_snapshot")
|
||||
var elapsed_milliseconds := (
|
||||
float(Time.get_ticks_usec() - started_microseconds) / 1000.0
|
||||
)
|
||||
_expect_true(
|
||||
elapsed_milliseconds < 1000.0,
|
||||
"20,000 snapshot cycles remain bounded",
|
||||
failures
|
||||
)
|
||||
return elapsed_milliseconds
|
||||
|
||||
|
||||
func _expect_string(
|
||||
actual: String,
|
||||
expected: String,
|
||||
label: String,
|
||||
failures: Array[String]
|
||||
) -> void:
|
||||
if actual != expected:
|
||||
failures.append("%s expected=%s actual=%s" % [label, expected, actual])
|
||||
|
||||
|
||||
func _expect_same(
|
||||
actual: Variant,
|
||||
expected: Variant,
|
||||
label: String,
|
||||
failures: Array[String]
|
||||
) -> void:
|
||||
if not is_same(actual, expected):
|
||||
failures.append(label)
|
||||
|
||||
|
||||
func _expect_true(condition: bool, label: String, failures: Array[String]) -> void:
|
||||
if not condition:
|
||||
failures.append(label)
|
||||
|
||||
|
||||
func _expect_false(condition: bool, label: String, failures: Array[String]) -> void:
|
||||
_expect_true(not condition, label, failures)
|
||||
@@ -0,0 +1 @@
|
||||
uid://cgx3uaof21ubs
|
||||
Reference in New Issue
Block a user