Gododin Gododin Documentation

Linked repository source

examples/slime_colony/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

# The slime_colony driver. Two modes share this scene:
#
# Interactive (no user args): the scene submits and checks each native frame,
# the sliders retune it live, Randomize reseeds the colony, and a
# 0.25 s timer refreshes the FPS and colony stats readout.
#
# Evaluation (--eval --size= --agents= --frames= --seed=): the driver takes
# the frame loop from the native class, brackets exactly submit_frame with the
# engine's microsecond clock over a warm window, measures frame-entry intervals,
# saves raw timing samples after measurement and captures the final rendered
# workload frame to user://slime.png, verifies CPU Image bytes against a fresh
# kernel render, replays the first 16 frames after a reseed and requires the
# identical digest, then tears every owner down cooperatively. It prints
# GODODIN_SLIME_COLONY_PASS ... and quits 0, or GODODIN_SLIME_COLONY_FAIL ...
# and quits 1.

const FIXED_PARAMS := {
	"turn_speed": 0.15,
	"sensor_distance": 9.0,
	"sensor_size": 3,
	"deposit": 1.0,
	"decay": 0.008,
	"jitter": 0.2,
	"gain": 6.0,
}

var sim: Node

# The native class's result vocabulary (declarations/slime_colony.odin):
# 0 success, positive godot.Call_Error pass-throughs, negative Slime_Error
# causes. _result_text decodes a returned code so a failure log names the
# cause instead of a bare integer.
const CALL_ERROR_NAMES := {
	1: "stale_generation",
	2: "wrong_role",
	3: "missing_interface",
	4: "unbound_operation",
	5: "invalid_handle",
	6: "invalid_owner",
	7: "too_many_arguments",
	8: "godot_call_failed",
}

const SLIME_ERROR_NAMES := {
	-1: "bounds",
	-2: "already_configured",
	-3: "not_configured",
	-4: "sensor_size_unsupported",
	-5: "params_invalid",
	-6: "colony_allocation",
	-7: "cpu_retirement_refused",
	-8: "cpu_cleanup_refused",
	-9: "self_unavailable",
	-10: "display_missing",
	-11: "display_not_sprite",
	-12: "display_set_texture",
	-13: "display_detach",
	-14: "image_create",
	-15: "texture_create",
	-16: "texture_release",
	-17: "image_release",
	-18: "kernel_failed",
	-19: "frame_array_create",
	-20: "image_set_data",
	-21: "texture_update",
	-22: "frame_array_release",
	-23: "readback_get_data",
	-24: "readback_scratch",
	-25: "readback_read",
	-26: "readback_release",
	-27: "readback_mismatch",
	-28: "image_metadata",
	-29: "image_length",
}


# The one-line status writer the interactive controls share.
func feedback(text: String) -> void:
	%Stats.text = text


func _result_text(operation: String, code: int) -> String:
	if code == 0:
		return operation
	if code > 0:
		return "%s call_error=%s" % [operation, CALL_ERROR_NAMES.get(code, "unknown=%d" % code)]
	return "%s error=%s" % [operation, SLIME_ERROR_NAMES.get(code, "unknown=%d" % code)]


var eval_mode := false
var profile_render := false
var start_on_click := false
var eval_started := false
var eval_size := 1024
var eval_agents := 60000
var eval_frames := 300
var eval_seed := 20260911
var eval_speed := 1.0
var eval_gain := 6.0

var frame_count := 0
var window_usec := 0.0
var measured_frames := 0
var digest_early := 0
var replaying := false
var finished := false
var reference_captured := false
var sample_intervals := PackedInt64Array()
var callback_samples := PackedInt64Array()
var unfocused_samples := 0
var previous_entry := 0
var capturing_final_frame := false


const RENDER_PROFILE_WIDTH := 9
var _render_profile_samples := PackedFloat64Array()
var _render_profile_count := 0
var _render_profile_viewport: RID
var _render_profile_active := false
var _render_wall_started := 0
var _render_wall_usec := 0
var _profile_stats_usec := 0
var _profile_stats_calls := 0


func _ready() -> void:
	sim = %Sim
	for arg in OS.get_cmdline_user_args():
		if arg == "--eval":
			eval_mode = true
		elif arg == "--eval-profile-render":
			profile_render = true
		elif arg == "--eval-start-on-click":
			start_on_click = true
		elif arg.begins_with("--size=") or arg.begins_with("--agents=") or arg.begins_with("--frames=") or arg.begins_with("--seed="):
			var value := arg.get_slice("=", 1)
			if not value.is_valid_int():
				_fail("invalid integer argument: %s" % arg)
				return
			if arg.begins_with("--size="):
				eval_size = int(value)
			elif arg.begins_with("--agents="):
				eval_agents = int(value)
			elif arg.begins_with("--frames="):
				eval_frames = int(value)
			else:
				eval_seed = int(value)
		elif arg.begins_with("--speed=") or arg.begins_with("--gain="):
			var value := arg.get_slice("=", 1)
			if not value.is_valid_float() or not is_finite(float(value)):
				_fail("invalid float argument: %s" % arg)
				return
			if arg.begins_with("--speed="):
				eval_speed = float(value)
			else:
				eval_gain = float(value)
		else:
			_fail("unknown argument: %s" % arg)
			return
	if profile_render and not eval_mode:
		_fail("render_profile_requires_eval")
		return
	if profile_render and not RenderingServer.is_on_render_thread():
		_fail("render_profile_requires_synchronous_rendering")
		return
	if eval_mode:
		if eval_frames < 32 or eval_frames > 100000 or eval_size < 32 or eval_size > 2048 or eval_agents < 1 or eval_agents > 1000000:
			_fail("eval requires frames 32..100000, size 32..2048 and agents 1..1000000")
			return
		_start_eval()
	else:
		_start_interactive()


func _start_interactive() -> void:
	var code: int = sim.call("configure", 1024, 1024, int(%AgentsSlider.value), eval_seed)
	if code != 0:
		push_error("GODODIN_SLIME_COLONY_FAIL " + _result_text("configure", code))
		get_tree().quit(1)
		return
	_fit_display(1024)
	if _apply_interactive_params() != 0 or sim.call("set_driver_driven", true) != 0:
		_fail("interactive configuration")
		return
	%AgentsSlider.value_changed.connect(_on_agents_changed)
	%EvapSlider.value_changed.connect(_on_params_changed)
	%SensorSlider.value_changed.connect(_on_params_changed)
	%SpeedSlider.value_changed.connect(_on_params_changed)
	%Randomize.pressed.connect(_on_randomize)
	# EX-SLIME: the nutrient input. Masks are checked-in authored assets; the
	# kernel takes one Odin-owned copy per import and the control decides when
	# that happens.
	%NutrientOption.add_item("None")
	%NutrientOption.add_item("Ring")
	%NutrientOption.add_item("Waves")
	%NutrientOption.item_selected.connect(_on_nutrient_selected)
	%AttractionSlider.value_changed.connect(_on_attraction_changed)
	%GpuToggle.toggled.connect(_on_gpu_toggled)
	_start_stats_timer()


func _start_eval() -> void:
	_start_render_profile()
	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
	sample_intervals.resize(eval_frames - int(eval_frames / 2))
	callback_samples.resize(sample_intervals.size())
	var code: int = sim.call("configure", eval_size, eval_size, eval_agents, eval_seed)
	if code != 0:
		push_error("GODODIN_SLIME_COLONY_FAIL " + _result_text("configure", code))
		get_tree().quit(1)
		return
	_fit_display(eval_size)
	code = _apply_eval_params()
	if code != 0:
		push_error("GODODIN_SLIME_COLONY_FAIL " + _result_text("set_params", code))
		get_tree().quit(1)
		return
	code = sim.call("set_driver_driven", true)
	if code != 0:
		push_error("GODODIN_SLIME_COLONY_FAIL " + _result_text("driver_handoff", code))
		get_tree().quit(1)
		return
	# Controls display the selected workload but cannot modify it mid-eval.
	%AgentsSlider.min_value = min(%AgentsSlider.min_value, eval_agents)
	%AgentsSlider.max_value = max(%AgentsSlider.max_value, eval_agents)
	%AgentsSlider.value = eval_agents
	%AgentsValue.text = str(eval_agents)
	%SpeedSlider.value = eval_speed
	%SpeedValue.text = "%.2f" % eval_speed
	for slider in [%AgentsSlider, %EvapSlider, %SensorSlider, %SpeedSlider]:
		slider.editable = false
	%Randomize.disabled = true
	if sim.call("set_motion", INF, 0.15, 0.4, 9.0, 3) == 0 or sim.call("set_field", 1.0, NAN, 0.2, 6.0) == 0:
		_fail("nonfinite parameters accepted")
		return
	if sim.call("get_frames") != 0 or sim.call("verify") != 0:
		_fail("initial frame-zero transport")
		return
	_start_stats_timer()


# _fit_display scales the scene-owned display sprite so the texture fills a
# centered square at 96% of the window height — visible wrap seams and a
# margin on every side, whatever grid size was configured.
func _fit_display(grid_size: int) -> void:
	var window_height := get_viewport_rect().size.y
	var display_scale := window_height * 0.96 / float(grid_size)
	%Display.scale = Vector2(display_scale, display_scale)


func _start_stats_timer() -> void:
	var timer := Timer.new()
	timer.wait_time = 0.25
	timer.autostart = true
	timer.timeout.connect(_update_stats)
	add_child(timer)


func _process(_delta: float) -> void:
	if finished:
		return
	if eval_mode:
		_eval_frame()
	elif _gpu != null and _gpu.is_valid_variant():
		# The GPU variant's interactive frame: agents advance on the CPU
		# (identical math), the field update dispatches to the GPU, the
		# readback result feeds the colony's presentation. Upload, dispatch,
		# and readback are each measured on the variant.
		if sim.call("advance_agents") != 0:
			_fail(_result_text("gpu agents", 1))
			return
		var current: PackedByteArray = sim.call("grid_bytes")
		var updated: PackedByteArray = _gpu.update(current)
		if sim.call("set_grid_bytes", updated) != 0:
			_fail(_result_text("gpu readback", 1))
			return
		var code: int = sim.call("render_frame")
		if code != 0:
			_fail(_result_text("gpu render", code))
	else:
		var code: int = sim.call("submit_frame")
		if code != 0:
			_fail(_result_text("interactive submit", code))


# --- DX-9: the explicitly selected GPU field-update variant. ---

var _gpu: SlimeGpuField = null
var _gpu_validated := false

func _on_gpu_toggled(pressed: bool) -> void:
	if eval_mode or finished:
		%GpuToggle.set_pressed_no_signal(false)
		return
	if pressed:
		_gpu = SlimeGpuField.new()
		if not _gpu.enable(1024, 1024, 0.008):
			feedback("GPU field unavailable on this device/renderer — staying CPU")
			_gpu = null
			%GpuToggle.set_pressed_no_signal(false)
			return
		_validate_gpu()
		if _gpu_validated:
			feedback("GPU field update on — validated %d steps bitwise (same model)" % SlimeGpuField.VALIDATION_STEPS)
		else:
			feedback("GPU field update on — VALIDATION FAILED: different model, no speedup claim")
	else:
		if _gpu != null:
			_gpu.disable()
		_gpu = null
		feedback("GPU field update off — CPU variant")

# Validation of the phase-3 equivalence only: N CPU field updates into a
# reference, then N GPU field updates from the same start field; bitwise
# comparison, tolerance 0. Agent state is deliberately untouched — the
# variant replaces only the field update, and any protocol that rewinds
# agent positions (which no op exposes) would compare different deposit
# footprints, not the field math.
func _validate_gpu() -> void:
	_gpu_validated = false
	var start: PackedByteArray = sim.call("grid_bytes")
	for _step in SlimeGpuField.VALIDATION_STEPS:
		if sim.call("field_update") != 0:
			return
	var reference: PackedByteArray = sim.call("grid_bytes")
	if sim.call("set_grid_bytes", start) != 0:
		return
	for _step in SlimeGpuField.VALIDATION_STEPS:
		var current: PackedByteArray = sim.call("grid_bytes")
		var updated: PackedByteArray = _gpu.update(current)
		if sim.call("set_grid_bytes", updated) != 0:
			return
	var final_gpu: PackedByteArray = sim.call("grid_bytes")
	var mismatched := 0
	if final_gpu.size() != reference.size():
		mismatched = -1
	else:
		for i in final_gpu.size():
			if final_gpu[i] != reference[i]:
				mismatched += 1
	if mismatched == 0:
		_gpu_validated = true
		print("GODODIN_SLIME_GPU_VALIDATION_PASS steps=%d mismatched_cells=0 tolerance=0 same_model=true" % SlimeGpuField.VALIDATION_STEPS)
	else:
		print("GODODIN_SLIME_GPU_VALIDATION_FAIL steps=%d mismatched_bytes=%d" % [SlimeGpuField.VALIDATION_STEPS, mismatched])


func _eval_frame() -> void:
	if capturing_final_frame:
		return
	if start_on_click and not eval_started:
		return
	# Windowed campaigns start only after the operator brings the window forward.
	# This leaves the seed, warm-up steps and measured workload unchanged.
	if frame_count == 0 and not replaying and DisplayServer.get_name() != "headless" and not get_window().has_focus():
		return
	var entry := Time.get_ticks_usec()
	if not replaying:
		if previous_entry != 0 and frame_count >= eval_frames / 2:
			sample_intervals[measured_frames] = entry - previous_entry
			if not get_window().has_focus():
				unfocused_samples += 1
		previous_entry = entry
		if profile_render:
			if frame_count >= eval_frames / 2:
				_sample_render_profile(measured_frames)
			_profile_stats_usec = 0
			_profile_stats_calls = 0
	var t0 := Time.get_ticks_usec()
	var code: int = sim.call("submit_frame")
	var t1 := Time.get_ticks_usec()
	if code != 0:
		_fail(_result_text("submit", code) + " frame=%d" % frame_count)
		return
	frame_count += 1
	if frame_count == 16 and not replaying:
		digest_early = sim.call("get_digest")
		reference_captured = true
	if replaying:
		if frame_count >= 16:
			_finish_eval()
		return
	if frame_count > eval_frames / 2:
		callback_samples[measured_frames] = t1 - t0
		window_usec += float(t1 - t0)
		measured_frames += 1
	if frame_count >= eval_frames:
		_finish_measurement()


func _finish_measurement() -> void:
	_stop_render_profile()
	# Protocol v2 keeps the full fixed-step trajectory and every measured frame,
	# but takes no screenshots inside the timed window. Unlike the old in-process
	# capture, waiting for draw completion captures the final submitted frame.
	# Freeze submission while awaiting the render; replay starts only afterward.
	capturing_final_frame = true
	if DisplayServer.get_name() != "headless":
		# Refresh the labels from the same final state before Godot draws it.
		_update_stats_display()
		await RenderingServer.frame_post_draw
		if finished or not is_inside_tree():
			return
		_save_screenshot()
		if finished:
			return
	capturing_final_frame = false
	print("GODODIN_SLIME_COLONY_MEASUREMENT protocol=2 timed_screenshots=0 final_capture=%s" % ["not_applicable" if DisplayServer.get_name() == "headless" else "after_draw"])
	_verify_and_replay()


func _verify_and_replay() -> void:
	var verify: int = sim.call("verify")
	if verify != 0:
		_fail(_result_text("verify", verify))
		return
	# Replacement is outside the timed window. Its frame-zero state and CPU
	# Image must agree before any next step can mask stale metadata or bytes.
	var alternate_agents := eval_agents + 1 if eval_agents < 1000000 else eval_agents - 1
	for count in [alternate_agents, eval_agents]:
		var change: int = sim.call("set_agent_count", count)
		if change != 0:
			_fail(_result_text("population replacement", change))
			return
		if sim.call("get_frames") != 0 or sim.call("verify") != 0:
			_fail("population replacement frame-zero transport")
			return
	var replay: int = sim.call("reseed", eval_seed)
	if replay != 0:
		_fail(_result_text("reseed", replay))
		return
	if sim.call("get_frames") != 0 or sim.call("verify") != 0:
		_fail("reseed frame-zero transport")
		return
	frame_count = 0
	replaying = true


func _finish_eval() -> void:
	if not _eval_pacing_ok():
		return
	if not reference_captured or sim.call("get_digest") != digest_early:
		_fail("replayed digest differs: determinism")
		return
	if sim.call("verify") != 0:
		_fail("replay CPU Image readback")
		return
	print("GODODIN_SLIME_COLONY_REPLAY_PASS frames=16 digest=%d" % digest_early)
	var teardown: int = sim.call("shutdown")
	if teardown != 0:
		_fail(_result_text("shutdown", teardown))
		return
	if sim.call("get_configured"):
		_fail("colony still configured after shutdown")
		return
	if %Display.texture != null:
		_fail("display retained texture after shutdown")
		return
	# Repeated construction retires both zero-reference native owners each time.
	for cycle in range(3):
		if sim.call("configure", 32, 32, 8, eval_seed) != 0 or sim.call("verify") != 0 or sim.call("shutdown") != 0:
			_fail("lifecycle cycle=%d" % cycle)
			return
		if sim.call("get_configured") or %Display.texture != null:
			_fail("lifecycle owner remained cycle=%d" % cycle)
			return
	if sim.call("shutdown") != 0:
		_fail("idempotent shutdown")
		return
	finished = true
	print("GODODIN_SLIME_COLONY_TEARDOWN_PASS configured=false display_texture=false cycles=3")
	var avg_usec := 0.0
	if measured_frames > 0:
		avg_usec = window_usec / float(measured_frames)
	if not _save_profile_samples() or not _save_render_profile():
		return
	var frame_avg := 0.0
	for interval in sample_intervals:
		frame_avg += interval
	frame_avg /= sample_intervals.size()
	sample_intervals.sort()
	var p95: int = sample_intervals[ceili(sample_intervals.size() * 0.95) - 1]
	print("GODODIN_SLIME_COLONY_PASS size=%d agents=%d frames=%d avg_usec=%.3f digest=%d measured_frames=%d frame_avg_usec=%.3f frame_p95_usec=%d" % [
		eval_size, eval_agents, eval_frames, avg_usec, digest_early, measured_frames, frame_avg, p95])
	get_tree().quit(0)


func _save_screenshot() -> void:
	if DisplayServer.get_name() == "headless":
		return
	var image := get_viewport().get_texture().get_image()
	if image == null or image.is_empty() or image.save_png("user://slime.png") != OK:
		_fail("screenshot capture/write failed")


func _apply_eval_params() -> int:
	return _push_params(eval_speed, FIXED_PARAMS.decay, deg_to_rad(22.5), eval_gain)


func _apply_interactive_params() -> int:
	return _push_params(%SpeedSlider.value, %EvapSlider.value, deg_to_rad(%SensorSlider.value),
		FIXED_PARAMS.gain)


# --- EX-SLIME: the nutrient mask input. ---

const NUTRIENT_MASKS := [null, preload("res://nutrient_ring.tres"), preload("res://nutrient_waves.tres")]

func _on_nutrient_selected(index: int) -> void:
	if eval_mode or finished:
		return
	if index == 0 or index >= NUTRIENT_MASKS.size():
		sim.call("clear_nutrient")
		_update_stats()
		return
	var mask: Resource = NUTRIENT_MASKS[index]
	var code: int = sim.call("set_nutrient", mask.bytes, int(mask.width), int(mask.height))
	if code != 0:
		_fail("nutrient_import code=%d" % code)
		return
	_update_stats()

func _on_attraction_changed(value: float) -> void:
	if eval_mode or finished:
		return
	%AttractionValue.text = str(int(value))
	var code: int = sim.call("set_attraction", value)
	if code != 0:
		_fail("attraction code=%d" % code)

func _push_params(speed: float, evaporation: float, sensor_angle: float, gain: float) -> int:
	var code: int = sim.call("set_motion",
		speed, FIXED_PARAMS.turn_speed, sensor_angle,
		FIXED_PARAMS.sensor_distance, FIXED_PARAMS.sensor_size)
	if code != 0:
		return code
	return sim.call("set_field",
		FIXED_PARAMS.deposit, evaporation, FIXED_PARAMS.jitter, gain)


func _on_agents_changed(value: float) -> void:
	%AgentsValue.text = str(int(value))
	var code: int = sim.call("set_agent_count", int(value))
	if code != 0:
		_fail(_result_text("set_agent_count", code))


func _on_params_changed(_value: float) -> void:
	%EvapValue.text = "%.3f" % %EvapSlider.value
	%SensorValue.text = "%.1f" % %SensorSlider.value
	%SpeedValue.text = "%.2f" % %SpeedSlider.value
	if _apply_interactive_params() != 0:
		_fail("interactive parameters rejected")


func _on_randomize() -> void:
	var seed := randi() % 2147483647
	var code: int = sim.call("reseed", seed)
	if code != 0:
		_fail(_result_text("reseed", code))


func _update_stats() -> void:
	if not profile_render:
		_update_stats_display()
		return
	var entered := Time.get_ticks_usec()
	_update_stats_display()
	_profile_stats_usec += Time.get_ticks_usec() - entered
	_profile_stats_calls += 1


func _update_stats_display() -> void:
	if eval_mode and start_on_click and not eval_started:
		%Stats.text = "Click the scene to start the focused evaluation"
		return
	if finished:
		return
	# The full-state digest is an explicit verification operation, not HUD work.
	%Stats.text = "FPS %d | agents %d | %dx%d | frame %d" % [
		Engine.get_frames_per_second(),
		sim.call("get_agent_count"),
		sim.call("get_width"),
		sim.call("get_height"),
		sim.call("get_frames"),
	]


func _fail(reason: String) -> void:
	finished = true
	push_error("GODODIN_SLIME_COLONY_FAIL %s" % reason)
	get_tree().quit(1)


func _exit_tree() -> void:
	_stop_render_profile()
	finished = true
	# The GPU variant owns RenderingDevice RIDs that only free_rid releases;
	# disable here so a quit with the toggle on exits with zero leaked RIDs.
	if _gpu != null:
		_gpu.disable()
		_gpu = null
	# Ordinary teardown no longer calls shutdown here: the native class
	# releases its engine-owned references in its PREDELETE notification, so
	# a scene that never calls shutdown exits with zero owned references.
	# The explicit shutdown method remains the eager path the teardown probe
	# exercises while the node is alive.


# Arrival N's interval covers entry N-1 to entry N; its callback is step N.
# Buffers are allocated before simulation; file I/O occurs after measurement.
func _save_profile_samples() -> bool:
	var file := FileAccess.open("user://slime-profile.csv", FileAccess.WRITE)
	if file == null:
		_fail("profile_samples_open")
		return false
	file.store_line("arrival_frame,callback_usec,previous_frame_interval_usec")
	for index in range(measured_frames):
		file.store_line("%d,%d,%d" % [int(eval_frames / 2) + 1 + index, callback_samples[index], sample_intervals[index]])
	file.flush()
	if file.get_error() != OK:
		_fail("profile_samples_write")
		return false
	print("GODODIN_SLIME_COLONY_PROFILE_FOCUS samples=%d unfocused=%d headless=%s" % [measured_frames, unfocused_samples, DisplayServer.get_name() == "headless"])
	if DisplayServer.get_name() != "headless" and unfocused_samples != 0:
		_fail("profile_window_not_focused")
		return false
	print("GODODIN_SLIME_COLONY_PROFILE_SAMPLES count=%d path=user://slime-profile.csv" % measured_frames)
	return true


# The windowed runner uses an explicit input event to establish foreground
# readiness before any simulation steps. Ordinary play and headless runs bypass it.
func _input(event: InputEvent) -> void:
	if eval_mode and start_on_click and not eval_started and event is InputEventMouseButton and event.pressed and get_window().has_focus():
		eval_started = true


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_SLIME_COLONY_EVAL_PACING vsync=%s max_fps=%d" % ["not_applicable" if headless else "disabled", Engine.max_fps])
	return true


# Opt-in instrumentation, outside the native callback timer. Dev5's GL query
# results use a three-frame ring (drivers/gles3/storage/utilities.cpp:351).
# These are buffered viewport observations, not timings for arrival_frame's
# callback. Their distributions may be compared; they cannot be added rowwise.
# Viewport spans exclude screen blit/swap (renderer_viewport.cpp:954). Engine
# timer queries and these reads add overhead included in whole-frame intervals.
func _start_render_profile() -> void:
	if not profile_render:
		return
	_render_profile_samples.resize((eval_frames - int(eval_frames / 2)) * RENDER_PROFILE_WIDTH)
	_render_profile_viewport = get_viewport().get_viewport_rid()
	if DisplayServer.get_name() != "headless":
		RenderingServer.viewport_set_measure_render_time(_render_profile_viewport, true)
		_render_profile_active = true
		RenderingServer.frame_pre_draw.connect(_render_profile_pre_draw)
		RenderingServer.frame_post_draw.connect(_render_profile_post_draw)


func _sample_render_profile(index: int) -> void:
	var offset := index * RENDER_PROFILE_WIDTH
	if _render_profile_active:
		_render_profile_samples[offset] = RenderingServer.viewport_get_measured_render_time_cpu(_render_profile_viewport)
		_render_profile_samples[offset + 1] = RenderingServer.viewport_get_measured_render_time_gpu(_render_profile_viewport)
		_render_profile_samples[offset + 2] = RenderingServer.get_frame_setup_time_cpu()
		_render_profile_samples[offset + 3] = RenderingServer.viewport_get_render_info(_render_profile_viewport, RenderingServer.VIEWPORT_RENDER_INFO_TYPE_VISIBLE, RenderingServer.VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME)
		_render_profile_samples[offset + 4] = RenderingServer.viewport_get_render_info(_render_profile_viewport, RenderingServer.VIEWPORT_RENDER_INFO_TYPE_SHADOW, RenderingServer.VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME)
		_render_profile_samples[offset + 5] = RenderingServer.viewport_get_render_info(_render_profile_viewport, RenderingServer.VIEWPORT_RENDER_INFO_TYPE_CANVAS, RenderingServer.VIEWPORT_RENDER_INFO_DRAW_CALLS_IN_FRAME)
	# Stats callbacks occur between frame entries; this cost does correspond to
	# arrival_frame's previous_frame_interval_usec, unlike buffered GL queries.
	_render_profile_samples[offset + 6] = _profile_stats_usec
	_render_profile_samples[offset + 7] = _profile_stats_calls
	_render_profile_samples[offset + 8] = _render_wall_usec
	_render_wall_usec = 0
	_render_profile_count += 1


# On the synchronous render path this wall bracket includes viewport work,
# blit/swap waiting, and earlier frame-drawn callbacks. It is not GPU execution
# time. Signal callbacks add overhead. The latest completed draw belongs to
# the previous frame interval; buffered viewport timestamps have a different lag.
func _render_profile_pre_draw() -> void:
	_render_wall_started = Time.get_ticks_usec()


func _render_profile_post_draw() -> void:
	_render_wall_usec = Time.get_ticks_usec() - _render_wall_started


func _stop_render_profile() -> void:
	if _render_profile_active:
		RenderingServer.viewport_set_measure_render_time(_render_profile_viewport, false)
		_render_profile_active = false
		RenderingServer.frame_pre_draw.disconnect(_render_profile_pre_draw)
		RenderingServer.frame_post_draw.disconnect(_render_profile_post_draw)


func _save_render_profile() -> bool:
	if not profile_render:
		return true
	if _render_profile_count != measured_frames or _render_profile_count == 0:
		_fail("render_profile_sample_count")
		return false
	var file := FileAccess.open("user://slime-render-profile.csv", FileAccess.WRITE)
	if file == null:
		_fail("render_profile_open")
		return false
	file.store_line("arrival_frame,viewport_cpu_ms,viewport_gpu_ms,setup_cpu_ms,visible_draw_calls,shadow_draw_calls,canvas_draw_calls,stats_usec,stats_calls,render_wall_usec")
	var totals := PackedFloat64Array()
	totals.resize(RENDER_PROFILE_WIDTH)
	var cpu_positive := 0
	var gpu_positive := 0
	var wall_positive := 0
	for index in range(_render_profile_count):
		var offset := index * RENDER_PROFILE_WIDTH
		for column in range(RENDER_PROFILE_WIDTH):
			var value := _render_profile_samples[offset + column]
			if not is_finite(value) or value < 0.0:
				_fail("invalid_render_profile_sample")
				return false
			totals[column] += value
		if _render_profile_samples[offset] > 0.0:
			cpu_positive += 1
		if _render_profile_samples[offset + 1] > 0.0:
			gpu_positive += 1
		if _render_profile_samples[offset + 8] > 0.0:
			wall_positive += 1
		file.store_line("%d,%.6f,%.6f,%.6f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f" % [int(eval_frames / 2) + 1 + index, _render_profile_samples[offset], _render_profile_samples[offset + 1], _render_profile_samples[offset + 2], _render_profile_samples[offset + 3], _render_profile_samples[offset + 4], _render_profile_samples[offset + 5], _render_profile_samples[offset + 6], _render_profile_samples[offset + 7], _render_profile_samples[offset + 8]])
	file.flush()
	if file.get_error() != OK:
		_fail("render_profile_write")
		return false
	var headless := DisplayServer.get_name() == "headless"
	# Preserve zeros in the CSV. An all-zero series is unavailable/unproven,
	# never proof of free rendering; headless has no rendering observation.
	var cpu_status := "unavailable" if headless or cpu_positive == 0 else "observed"
	var gpu_status := "unavailable" if headless or gpu_positive == 0 else "observed"
	var wall_status := "unavailable" if headless or wall_positive == 0 else "observed"
	print("GODODIN_SLIME_COLONY_RENDER_PROFILE samples=%d cpu_status=%s gpu_status=%s cpu_positive=%d gpu_positive=%d viewport_cpu_ms=%.6f viewport_gpu_ms=%.6f setup_cpu_ms=%.6f stats_avg_usec=%.6f stats_calls=%d render_wall_usec=%.6f wall_status=%s wall_positive=%d render_wall_scope=pre_to_post_draw render_wall_swap_included=true lag=renderer_buffered viewport_swap_included=false overhead=included headless=%s path=user://slime-render-profile.csv" % [_render_profile_count, cpu_status, gpu_status, cpu_positive, gpu_positive, totals[0] / _render_profile_count, totals[1] / _render_profile_count, totals[2] / _render_profile_count, totals[6] / _render_profile_count, int(totals[7]), totals[8] / _render_profile_count, wall_status, wall_positive, headless])
	return true