Gododin Gododin Documentation

Linked repository source

examples/bunny_mark/project/main.gd

This read-only view comes from the file in the Gododin repository. It retains its design, example or evidence context.

Download the original file · Return to the guide

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