OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_graphics_presets.gd

132 lines · gd ref:
extends SceneTree
## Gate: graphics presets (Low / Medium / High) and Cloud Quality
## (hyper-games #93 §2; Medium + cloud tiers 2026-10-03).
##
## Measured on a GTX 1060 before it throttled (#95 correction comment): GPU frame
## 18 ms = opaque pass 6.8 (terrain material) + SunshineClouds2 compositor 5.9 +
## sky 1.7 + shadows 0.4. Clouds are ~⅓ of the frame and the largest optional
## cost, but were only a map export (map_mountain) or a scene node (map_dc_terrain)
## — no player control. Low drops clouds and the per-pixel terrain material.
##
## A preset is an ACTION, not a stored value: choosing one writes the keys it
## covers as player overrides; it reads as selected only while every one matches.
##
## Run: HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_graphics_presets.gd

var ok := true
var _ran := false


func _p(c: bool, m: String) -> void:
	print(("ok   " if c else "FAIL: ") + m)
	if not c:
		ok = false


func _process(_delta: float) -> bool:
	if _ran:
		return true
	_ran = true
	_run()
	print("GATE graphics_presets: %s" % ("PASS" if ok else "FAIL"))
	quit(0 if ok else 1)
	return true


func _run() -> void:
	var s: Node = root.get_node_or_null(^"/root/Settings")
	_p(s != null, "Settings autoload present")
	if s == null:
		return
	var saved: Dictionary = s._overrides.duplicate()

	print("-- schema")
	var preset_row: Dictionary = s.schema_for("preset")
	var clouds_row: Dictionary = s.schema_for("clouds")
	_p(not preset_row.is_empty() and preset_row.get("choices", []) == ["Low", "Medium", "High"],
		"a Preset row offers Low / Medium / High")
	# Cloud Quality = SunshineClouds2 resolution_scale. Measured on an RTX 2070, mountain
	# clouds (scenes/test/_probe_cloud_quality_cost.gd): off 0.15, Quarter 2.85,
	# Half 4.66, Native 11.26 GPU ms.
	_p(not clouds_row.is_empty() and clouds_row.get("type", "") == "enum"
		and clouds_row.get("choices", []) == ["Off", "Quarter res", "Half res", "Full res"]
		and clouds_row.get("values", []) == [-1, 2, 1, 0] and int(clouds_row.get("default", -9)) == 2,
		"Clouds Off / Quarter / Half / Full res, Half by default")
	_p(s.SCHEMA[0].get("key", "") == "preset", "Preset is the first row of the panel")

	print("-- presets")
	s._overrides.clear()
	_p(int(s.get_value("preset")) == 2, "fresh settings read as High (High = today's defaults)")
	s.set_value("preset", 0)
	_p(int(s.get_value("terrain_material")) == 1, "Low: Vertex terrain material")
	_p(int(s.get_value("clouds")) == 0, "Low: clouds off")
	_p(is_equal_approx(float(s.resolved_value("render_scale")), 0.75), "Low: 75% render scale")
	_p(int(s.get_value("aa")) == 0 and int(s.get_value("bloom")) == 1, "Low: MSAA off, bloom Low")
	_p(int(s.get_value("preset")) == 0, "and it reads as Low")
	s.set_value("preset", 1)
	_p(int(s.get_value("terrain_material")) == 1 and int(s.resolved_value("clouds")) == 2
		and is_equal_approx(float(s.resolved_value("render_scale")), 1.0) and int(s.get_value("aa")) == 0
		and int(s.get_value("bloom")) == 2 and int(s.resolved_value("texture_res")) == 1024
		and int(s.get_value("surface_detail")) == 0,
		"Medium: Vertex / Quarter clouds / 100% / MSAA off / bloom High / 1024 / normal maps")
	_p(int(s.get_value("preset")) == 1, "and it reads as Medium")
	s.set_value("aa", 3)
	_p(int(s.get_value("preset")) == -1, "changing any covered row reads as custom (no preset highlighted)")
	s.set_value("preset", 2)
	_p(int(s.get_value("terrain_material")) == 0 and int(s.resolved_value("clouds")) == 1
		and is_equal_approx(float(s.resolved_value("render_scale")), 1.0) and int(s.get_value("aa")) == 1,
		"High restores Full / Half clouds / 100% / MSAA 2X")
	_p(int(s.get_value("preset")) == 2, "and reads as High")
	s.set_value("clouds", 3)
	_p(int(s.get_value("preset")) == -1, "Ultra clouds is no preset (high-end, set by hand)")
	_p(not s._overrides.has("preset"), "the preset itself is never stored")

	print("-- saved bool migrates")
	var sp: String = s.SAVE_PATH
	var had_file: bool = FileAccess.file_exists(sp)
	var keep: String = FileAccess.get_file_as_string(sp) if had_file else ""
	for case: Array in [[true, 2], [false, 0]]:
		var f := FileAccess.open(sp, FileAccess.WRITE)
		f.store_string(JSON.stringify({"clouds": case[0]}))
		f.close()
		s.load_settings()
		_p(int(s.get_value("clouds")) == int(case[1]),
			"a saved clouds=%s loads as %d (not int(bool))" % [str(case[0]), case[1]])
	if had_file:
		var f2 := FileAccess.open(sp, FileAccess.WRITE)
		f2.store_string(keep)
		f2.close()
	else:
		DirAccess.remove_absolute(ProjectSettings.globalize_path(sp))

	print("-- clouds switch")
	var we := WorldEnvironment.new()
	we.environment = Environment.new()
	we.compositor = Compositor.new()
	var fx: CompositorEffect = SunshineCloudsGD.new()
	fx.enabled = true
	var broken: CompositorEffect = SunshineCloudsGD.new()
	broken.enabled = false   # as the addon leaves it when it fails to initialise
	var other := CompositorEffect.new()
	other.enabled = true
	var list: Array[CompositorEffect] = [fx, broken, other]
	we.compositor.compositor_effects = list
	root.add_child(we)
	s._apply_clouds(-1)
	_p(not fx.enabled, "clouds off disables the SunshineClouds effect")
	_p(other.enabled, "other compositor effects are untouched")
	s._apply_clouds(1)
	_p(fx.enabled and int(fx.get("resolution_scale")) == 1, "clouds High re-enables it at Half resolution")
	_p(not broken.enabled, "an effect the ADDON disabled (failed init) is never forced on")
	s._apply_clouds(0)
	_p(int(fx.get("resolution_scale")) == 0, "Ultra = Native resolution")
	s.set_value("clouds", 0)
	s.apply_settings()
	_p(not fx.enabled, "apply_settings applies the clouds row")
	s.set_value("clouds", 1)
	s.apply_settings()
	_p(fx.enabled and int(fx.get("resolution_scale")) == 2, "and back on at Low = Quarter resolution")
	we.queue_free()

	s._overrides = saved