extends Node2D # Equal seeded identities, texture footprint and fixed-step physics across # three transport paths. Driver CPU excludes engine rendering; actual frame # intervals are reported separately. Verification and capture are untimed. enum Mode { GDSCRIPT, NODE, FIELD, BULK_GD } const MODE_NAMES := ["gd", "node", "field", "gd-bulk"] const POPULATION_SEED := GdBunnies.POPULATION_SEED var mode: int = Mode.GDSCRIPT var count := 2500 var speed_scale := 1.0 var gravity_on := true ## The authored scene preset (EX-BUNNY): initial count/motion/seed plus the ## scene-owned texture and draw scale every arm shares. Explicit Apply reads ## the assigned Resource; Reset restores the last-applied values. Canonical ## evaluation runs never read it. @export var preset: Resource var _population_seed := POPULATION_SEED var _texture: Texture2D = GdBunnies.TEXTURE var _draw_scale := GdBunnies.DRAW_SCALE var _applied := {} var field_ready := false var native_bunnies: Array[Sprite2D] = [] var failed := false var stopping := false var steps := 0 var canonical_history := true var eval_active := false var eval_mode := "gd" var eval_count := 2500 var eval_frames := 1200 var eval_warm := 0 var eval_timed := 0 var eval_done_warm := 0 var eval_done_timed := 0 var eval_window_usec := 0.0 var frame_intervals := PackedFloat64Array() var previous_frame_usec := 0 var start_on_click := false var eval_started := false var unfocused_samples := 0 func _ready() -> void: # Retained staging is reused after direct server-thread consumption. if not RenderingServer.is_on_render_thread(): _fail("separate_render_thread_unsupported") return var seen := {} for arg in OS.get_cmdline_user_args(): var key: String = arg.split("=", true, 1)[0] if seen.has(key): _fail("duplicate_argument=" + key) return seen[key] = true if arg == "--eval": eval_active = true elif arg == "--eval-start-on-click": start_on_click = true elif arg.begins_with("--mode="): eval_mode = arg.trim_prefix("--mode=") elif arg.begins_with("--count=") and arg.trim_prefix("--count=").is_valid_int(): eval_count = int(arg.trim_prefix("--count=")) elif arg.begins_with("--frames=") and arg.trim_prefix("--frames=").is_valid_int(): eval_frames = int(arg.trim_prefix("--frames=")) else: _fail("unknown_or_invalid_argument=" + arg) return if not _create_canvas_quad(): return %ModeOption.add_item("GDScript loop", Mode.GDSCRIPT) %ModeOption.add_item("Native per-node", Mode.NODE) %ModeOption.add_item("Native staged", Mode.FIELD) %ModeOption.add_item("GDScript bulk", Mode.BULK_GD) %ModeOption.item_selected.connect(_on_mode_selected) %CountSlider.value_changed.connect(_on_count_changed) %SpeedSlider.value_changed.connect(_on_speed_changed) %GravityCheck.toggled.connect(_on_gravity_toggled) if not eval_active: %ApplyPreset.pressed.connect(_on_apply_preset) %ResetPreset.pressed.connect(_on_reset_preset) $StatsTimer.timeout.connect(_update_stats) if eval_active: Engine.max_fps = 0 if DisplayServer.get_name() != "headless": DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED, get_window().get_window_id()) if not _eval_pacing_ok(): return if eval_mode not in MODE_NAMES or eval_count < 1 or eval_count > 20000 or eval_frames < 4 or eval_frames > 100000: _fail("bad_arguments") return count = eval_count eval_warm = eval_frames / 2 eval_timed = eval_frames - eval_warm frame_intervals.resize(eval_timed - 1) %ModeOption.disabled = true %CountSlider.editable = false %SpeedSlider.editable = false %GravityCheck.disabled = true if not _switch_mode(MODE_NAMES.find(eval_mode)): return print("GODODIN_BUNNY_MARK_EVAL_START workload=bunny-mark-v2 mode=%s count=%d warm=%d timed=%d" % [eval_mode,count,eval_warm,eval_timed]) else: _apply_preset_values() _switch_mode(Mode.GDSCRIPT) %CountSlider.set_value_no_signal(count) %CountLabel.text = str(count) _update_stats() # A scene-owned ArrayMesh uses the same local corners and UV direction as # Sprite2D: screen top-left is (-7,-7) with UV(0,0). Dev5 QuadMesh/PlaneMesh # FACE_Z generates an inverted V mapping for this CanvasItem convention. func _create_canvas_quad() -> bool: var arrays := [] arrays.resize(Mesh.ARRAY_MAX) arrays[Mesh.ARRAY_VERTEX] = PackedVector3Array([Vector3(-7,-7,0),Vector3(7,-7,0),Vector3(7,7,0),Vector3(-7,7,0)]) arrays[Mesh.ARRAY_TEX_UV] = PackedVector2Array([Vector2(0,0),Vector2(1,0),Vector2(1,1),Vector2(0,1)]) arrays[Mesh.ARRAY_INDEX] = PackedInt32Array([0,1,2,0,2,3]) var quad := ArrayMesh.new() quad.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) $Field/MMI.multimesh.mesh = quad return _verify_canvas_quad() func _verify_canvas_quad() -> bool: var quad := $Field/MMI.multimesh.mesh as ArrayMesh if quad == null or quad.get_surface_count() != 1 or quad.surface_get_primitive_type(0) != Mesh.PRIMITIVE_TRIANGLES: return _fail("quad_surface") var arrays := quad.surface_get_arrays(0) var vertices: PackedVector3Array = arrays[Mesh.ARRAY_VERTEX] var uv: PackedVector2Array = arrays[Mesh.ARRAY_TEX_UV] var indices: PackedInt32Array = arrays[Mesh.ARRAY_INDEX] if vertices.size() != 4 or uv.size() != 4 or indices != PackedInt32Array([0,1,2,0,2,3]): return _fail("quad_extent") for i in 4: # Each uploaded corner must map the sprite's entire 14px rectangle to # positive-down UVs. This checks the decoded engine mesh, not a local # duplicate of the staging input. if vertices[i].z != 0 or absf(vertices[i].x) != 7 or absf(vertices[i].y) != 7 or uv[i] != Vector2((vertices[i].x+7)/14,(vertices[i].y+7)/14): return _fail("quad_uv") if vertices[0] != Vector3(-7,-7,0) or vertices[1] != Vector3(7,-7,0) or vertices[2] != Vector3(7,7,0) or vertices[3] != Vector3(-7,7,0): return _fail("quad_geometry") return true func _physics_process(_delta: float) -> void: if not eval_active and not failed and not stopping: _step() func _process(_delta: float) -> void: if not eval_active or failed or stopping: return if start_on_click and not eval_started: return if steps == 0 and not get_window().has_focus(): return if eval_done_warm < eval_warm: if not _step(): return eval_done_warm += 1 return var entry := Time.get_ticks_usec() if not get_window().has_focus(): unfocused_samples += 1 if previous_frame_usec != 0: frame_intervals[eval_done_timed - 1] = float(entry - previous_frame_usec) previous_frame_usec = entry var submitted := Time.get_ticks_usec() if not _step(): return eval_window_usec += float(Time.get_ticks_usec() - submitted) eval_done_timed += 1 if eval_done_timed >= eval_timed: stopping = true _finish_eval() func _step() -> bool: match mode: Mode.GDSCRIPT: $Bunnies.step_fixed() Mode.BULK_GD: $Bulk.step_fixed() Mode.NODE: for bunny in native_bunnies: if not _check(bunny.call("step_fixed"), "node_step"): return false Mode.FIELD: if not _check($Field.call("submit_frame"), "field_step"): return false steps += 1 canonical_history = canonical_history and gravity_on and speed_scale == 1.0 return true func _finish_eval() -> void: if not _eval_pacing_ok(): return print("GODODIN_BUNNY_MARK_PROFILE_FOCUS samples=%d unfocused=%d" % [eval_done_timed, unfocused_samples]) if unfocused_samples != 0: _fail("profile_window_not_focused") return if not _verify_population(): return # Exercise repeated field allocation and same-seed replay outside timing. if mode == Mode.FIELD: # Verification must release every readback before the next retained write. for probe in 2: if not _check($Field.call("submit_frame"), "reuse_submit") or not _check($Field.call("verify"), "reuse_verify"): return print("GODODIN_BUNNY_MARK_RETAINED_REUSE_PASS cycles=2") for resized in [maxi(1, count / 2), count]: if not _check($Field.call("set_count", resized), "resize"): return if $Field.call("frames_submitted") != 0 or $Field.call("count") != resized: _fail("resize_state") return if not _check($Field.call("submit_frame"), "resize_submit") or not _check($Field.call("verify"), "resize_verify"): return # Restore the measured final state so the image and labels describe it. if not _check($Field.call("set_count", count), "restore_count"): return for _frame in steps: if not _check($Field.call("submit_frame"), "restore_step"): return if not _check($Field.call("verify"), "restore_verify"): return _update_stats() if DisplayServer.get_name() == "headless": _fail("rendered_campaign_requires_display") return await RenderingServer.frame_post_draw var image := get_viewport().get_texture().get_image() if image == null or image.is_empty() or image.save_png("user://bunny-%s.png" % eval_mode) != OK: _fail("screenshot") return var frame_avg := 0.0 var samples := FileAccess.open("user://bunny-profile.csv", FileAccess.WRITE) if samples == null: _fail("profile_samples_open") return samples.store_line("arrival_frame,previous_frame_interval_usec") for index in frame_intervals.size(): samples.store_line("%d,%.0f" % [eval_warm + 2 + index, frame_intervals[index]]) samples.flush() if samples.get_error() != OK: _fail("profile_samples_write") return for sample in frame_intervals: frame_avg += sample frame_avg /= maxi(1, frame_intervals.size()) frame_intervals.sort() var p95 := frame_intervals[mini(frame_intervals.size()-1, int(ceil(frame_intervals.size()*0.95))-1)] if not frame_intervals.is_empty() else 0.0 if not _shutdown(): return print("GODODIN_BUNNY_MARK_PASS workload=bunny-mark-v2 mode=%s count=%d frames=%d measured_frames=%d submit_avg_usec=%.3f frame_avg_usec=%.3f frame_p95_usec=%.3f frame_samples=%d verified=%d" % [eval_mode,count,steps,eval_timed,eval_window_usec/eval_timed,frame_avg,p95,frame_intervals.size(),count]) print("GODODIN_BUNNY_MARK_TEARDOWN_PASS staging_live=false count=0") get_tree().quit(0) func _input(event: InputEvent) -> void: if eval_active and start_on_click and not eval_started and event is InputEventMouseButton and event.pressed and get_window().has_focus(): eval_started = true func _verify_population() -> bool: match mode: Mode.BULK_GD: if $Bulk.state.size() != count * 5 or $Bulk/MMI.multimesh.instance_count != count or $Bulk/MMI.texture != _texture or not $Bulk/MMI.is_visible_in_tree(): return _fail("bulk_drawable") if not _check($Field.call("verify_buffer", $Bulk.state_buffer(), steps, POPULATION_SEED), "bulk_replay"): return false Mode.GDSCRIPT: if $Bunnies.sprites.size() != count: return _fail("gd_count") if not _check($Field.call("verify_buffer", $Bunnies.state_buffer(), steps, POPULATION_SEED), "gd_replay"): return false for i in count: if not _verify_sprite($Bunnies.sprites[i], i): return false Mode.NODE: if native_bunnies.size() != count: return _fail("node_count") for i in count: if not _verify_sprite(native_bunnies[i], i) or not _check(native_bunnies[i].call("verify", i, steps, POPULATION_SEED), "node_replay"): return false Mode.FIELD: if not field_ready or not $Field.visible or $Field.call("count") != count or RenderingServer.multimesh_get_instance_count($Field/MMI.multimesh.get_rid()) != count or $Field/MMI.texture != _texture or not $Field/MMI.is_visible_in_tree() or not _verify_canvas_quad(): return _fail("field_drawable") if not _check($Field.call("verify"), "field_replay"): return false return true func _verify_sprite(sprite: Sprite2D, index: int) -> bool: var p := GdBunnies.parameter(index) if not sprite.is_inside_tree() or not sprite.is_visible_in_tree() or sprite.texture != _texture or sprite.scale != Vector2.ONE * (p[4] * _draw_scale): return _fail("sprite_drawable index=%d" % index) return true func _switch_mode(target: int) -> bool: $Field.visible = false $Bulk.visible = false $Bunnies.clear_all() _clear_native_bunnies() if field_ready: if not _check($Field.call("shutdown"), "mode_shutdown"): return false field_ready = false mode = target steps = 0 canonical_history = true match mode: Mode.GDSCRIPT: $Bunnies.set_count(count, _population_seed, _texture, _draw_scale) Mode.BULK_GD: $Bulk.set_count(count, _population_seed, _texture, _draw_scale) $Bulk.visible = true Mode.NODE: for i in count: var p := GdBunnies.parameter(i, _population_seed) var bunny := ClassDB.instantiate("Bunny") as Sprite2D if bunny == null: return _fail("native_class") bunny.texture = _texture bunny.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST bunny.scale = Vector2.ONE * (p[4] * _draw_scale) add_child(bunny) native_bunnies.append(bunny) if not _check(bunny.call("configure",1280.0,720.0), "configure") or not _check(bunny.call("launch",p[0],p[1],p[2],p[3]), "launch"): return false Mode.FIELD: if not _check($Field.call("setup",count,_population_seed), "setup"): return false field_ready = true $Field.visible = true %ModeOption.select(mode) return _apply_motion() func _clear_native_bunnies() -> void: for bunny in native_bunnies: bunny.free() native_bunnies.clear() func _on_mode_selected(item: int) -> void: _switch_mode(item) _update_stats() func _on_count_changed(value: float) -> void: count = int(value) %CountLabel.text = str(count) _switch_mode(mode) _update_stats() func _on_speed_changed(value: float) -> void: speed_scale = Vector2(value, 0).x # Match native f32 admission before stepping. %SpeedLabel.text = "%.2f" % value _apply_motion() func _on_gravity_toggled(value: bool) -> void: gravity_on = value _apply_motion() # --- EX-BUNNY: explicit Apply/Reset over the authored scene preset. --- func _apply_preset_values() -> void: if preset != null: count = clampi(int(preset.count), 1, 20000) speed_scale = float(preset.speed_scale) gravity_on = bool(preset.gravity_on) _population_seed = int(preset.population_seed) _texture = preset.texture if preset.texture != null else GdBunnies.TEXTURE _draw_scale = float(preset.draw_scale) if float(preset.draw_scale) > 0.0 else GdBunnies.DRAW_SCALE %CountSlider.set_value_no_signal(count) %CountLabel.text = str(count) %SpeedSlider.set_value_no_signal(speed_scale) %SpeedLabel.text = "%.2f" % speed_scale %GravityCheck.set_pressed_no_signal(gravity_on) _applied = {"count": count, "speed_scale": speed_scale, "gravity_on": gravity_on, "seed": _population_seed} func _on_apply_preset() -> void: # Explicit Apply reads the current assigned Resource — an Inspector edit # never reaches the simulation on its own. _apply_preset_values() _switch_mode(mode) _update_stats() func _on_reset_preset() -> void: # Reset restores the last-applied preset values, discarding interactive # drift without re-reading the Resource. if _applied.is_empty(): return count = int(_applied.count) speed_scale = float(_applied.speed_scale) gravity_on = bool(_applied.gravity_on) _population_seed = int(_applied.seed) %CountSlider.set_value_no_signal(count) %CountLabel.text = str(count) %SpeedSlider.set_value_no_signal(speed_scale) %SpeedLabel.text = "%.2f" % speed_scale %GravityCheck.set_pressed_no_signal(gravity_on) _switch_mode(mode) _update_stats() func _apply_motion() -> bool: var gravity := 900.0 if gravity_on else 0.0 match mode: Mode.GDSCRIPT: $Bunnies.speed_scale = speed_scale $Bunnies.gravity = gravity Mode.BULK_GD: $Bulk.speed_scale = speed_scale $Bulk.gravity = gravity Mode.NODE: for bunny in native_bunnies: if not _check(bunny.call("set_motion",speed_scale,gravity), "motion"): return false Mode.FIELD: if field_ready and not _check($Field.call("set_profile",speed_scale,gravity), "profile"): return false return true func _update_stats() -> void: if eval_active and start_on_click and not eval_started: %StatsLabel.text = "Click the scene to start the focused evaluation" return %FpsLabel.text = "FPS %d" % Engine.get_frames_per_second() var verifiable := canonical_history and gravity_on and speed_scale == 1.0 if mode == Mode.FIELD and field_ready: verifiable = verifiable and $Field.call("verifiable") %StatsLabel.text = "%s · %d bunnies · %s" % [MODE_NAMES[mode],count,"canonical replay" if verifiable else "custom history"] func _check(code: int, context: String) -> bool: return true if code == 0 else _fail("%s code=%d" % [context,code]) func _fail(detail: String) -> bool: if not failed: failed = true push_error("GODODIN_BUNNY_MARK_FAIL " + detail) get_tree().quit(1) return false func _shutdown() -> bool: var code: int = $Field.call("shutdown") field_ready = false if code != 0 or $Field.call("staging_live") or $Field.call("count") != 0: return _fail("shutdown code=%d" % code) return true func _exit_tree() -> void: # Children leave first; this field is still alive during parent's exit and # shutdown needs only retained native owners, never its child resources. _shutdown() func _eval_pacing_ok() -> bool: var headless := DisplayServer.get_name() == "headless" if Engine.max_fps != 0 or (not headless and DisplayServer.window_get_vsync_mode(get_window().get_window_id()) != DisplayServer.VSYNC_DISABLED): _fail("eval_pacing_changed") return false print("GODODIN_BUNNY_MARK_EVAL_PACING vsync=%s max_fps=%d" % ["not_applicable" if headless else "disabled", Engine.max_fps]) return true