OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / _probe_pom_level.gd

156 lines · gd ref:
extends SceneTree
## PROBE (not a gate) — the Parallax surface-detail tier on a REAL level: map_flat's empty
## DCLevel + one Bricks105 brush wall made through the journal (as the player would),
## LevelPackMaterial switched Normal maps <-> Parallax with set_detail (NOT the Settings
## row, so the player's saved settings are untouched). Per tier and pose: the viewport's
## measured GPU time (median of interleaved rounds, 2x supersampled) + a screenshot.
##   DISPLAY=:0 <godot> --path . --rendering-driver vulkan --script res://scenes/test/_probe_pom_level.gd -- --out DIR

const ProbeWindow := preload("res://scenes/test/probe_window.gd")
const TIERS := ["normal_maps", "parallax"]
# The wall: x -3..3, y 0..3, z -1..-0.5; its +Z face is the one we look at.
const POSES := [
	["grazing", Vector3(-2.6, 1.4, 0.15), Vector3(1.5, 1.2, -0.5)],
	["close", Vector3(0.3, 1.5, 0.9), Vector3(0.0, 1.4, -0.5)],
	["far", Vector3(0.0, 1.5, 16.0), Vector3(0.0, 1.5, -0.5)],
]
const WARM := 30
const MEASURE := 90
const ROUNDS := 3
const SUPERSAMPLE := 2.0
const SIZE := Vector2i(1600, 900)

var _out := "/tmp/pom_level"
var _tex := "ambientcg/Bricks105"   # --tex <pack/name> to render another material
var _ws := ""
var _next_i3 := 0
var _ready_frames := 0
var _setup_frames := 0
var _map: Node = null
var _cam: Camera3D
var _vp: RID
var _case := 0
var _round := 0
var _f := 0
var _acc := 0.0
var _samples := {}


func _box(lo: Vector3, hi: Vector3) -> Array:
	return [1, 0, 0, hi.x, -1, 0, 0, -lo.x, 0, 1, 0, hi.y, 0, -1, 0, -lo.y, 0, 0, 1, hi.z, 0, 0, -1, -lo.z]


func _initialize() -> void:
	var args := OS.get_cmdline_user_args()
	var i := args.find("--out")
	if i >= 0 and i + 1 < args.size():
		_out = args[i + 1]
	var ti := args.find("--tex")
	if ti >= 0 and ti + 1 < args.size():
		_tex = args[ti + 1]
	DirAccess.make_dir_recursive_absolute(_out)
	DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
	DisplayServer.window_set_size(SIZE)
	DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
	_ws = ProbeWindow.free_workspace()
	print("PROBE i3 workspace: %s" % (_ws if _ws != "" else "(none)"))
	_map = (load("res://scenes/maps/map_flat.tscn") as PackedScene).instantiate()
	root.add_child(_map)


func _process(_d: float) -> bool:
	if _ready_frames < 20:
		if _ws != "" and _ready_frames == 0 and Time.get_ticks_msec() >= _next_i3:
			_next_i3 = Time.get_ticks_msec() + 1000
			ProbeWindow.move_to(_ws, SIZE)
		if ProbeWindow.drawing(root):
			_ready_frames += 1
		elif Time.get_ticks_msec() > 30000:
			print("PROBE FAIL window never drew (size %s)" % root.size)
			quit(1)
			return true
		return false
	_setup_frames += 1
	if _setup_frames == 10:
		var persist: Node = _map.get("_persist")
		var ft: Array = []
		for k in 6:
			ft.append({"tex": _tex, "mode": 1})
		persist.record("brush.create", {"planes": _box(Vector3(-3, 0, -1), Vector3(3, 3, -0.5)), "proj": [],
			"material_id": 1, "level_id": 0, "sky": false, "face_tex": ft})
		persist.refold()
		_cam = Camera3D.new()
		_cam.fov = 70
		root.add_child(_cam)
		_cam.make_current()
		var sun := DirectionalLight3D.new()
		sun.rotation_degrees = Vector3(-25, -70, 0)      # low, from the side: relief reads
		root.add_child(sun)
		root.scaling_3d_mode = Viewport.SCALING_3D_MODE_BILINEAR
		root.scaling_3d_scale = SUPERSAMPLE
		root.msaa_3d = Viewport.MSAA_DISABLED
		root.use_taa = false
		_vp = root.get_viewport_rid()
		RenderingServer.viewport_set_measure_render_time(_vp, true)
		return false
	if _setup_frames < 60:
		var packs: Variant = _map.get("_packs")
		if packs != null:
			packs.sync()
		return false
	if _setup_frames == 60:
		var mi := (_map.get("level") as Node).get_node("LevelMesh") as MeshInstance3D
		print("PROBE level aabb %s, packs %s" % [mi.get_aabb(), str(_map.get("_packs"))])
		_set_case()
		return false
	_f += 1
	if _f > WARM:
		_acc += RenderingServer.viewport_get_measured_render_time_gpu(_vp)
	if _f == WARM + MEASURE:
		var pose: String = POSES[_case / TIERS.size()][0]
		var tier: String = TIERS[_case % TIERS.size()]
		var ms := _acc / MEASURE
		if ms <= 0.0:
			print("PROBE FAIL %s/%s measured 0 ms" % [pose, tier])
			quit(1)
			return true
		var key := "%s/%s" % [pose, tier]
		if not _samples.has(key):
			_samples[key] = []
		_samples[key].append(ms)
		if _round == 0:
			root.get_texture().get_image().save_png("%s/%s_%s.png" % [_out, pose, tier])
		_case += 1
		if _case >= POSES.size() * TIERS.size():
			_case = 0
			_round += 1
			if _round >= ROUNDS:
				_report()
				quit(0)
				return true
		_set_case()
	return false


func _set_case() -> void:
	var p: Array = POSES[_case / TIERS.size()]
	(_map.get("_packs") as LevelPackMaterial).set_detail(_case % TIERS.size())
	_cam.look_at_from_position(p[1], p[2])
	_f = 0
	_acc = 0.0


func _report() -> void:
	var px := Vector2(root.size) * SUPERSAMPLE
	print("PROBE GPU ms, median of %d interleaved rounds x %d frames, %dx%d shaded px" % [ROUNDS, MEASURE, int(px.x), int(px.y)])
	for p: Array in POSES:
		var base := 0.0
		for t: String in TIERS:
			var v: Array = _samples["%s/%s" % [p[0], t]].duplicate()
			v.sort()
			var med: float = v[v.size() / 2]
			if t == "normal_maps":
				base = med
			print("PROBE %-8s %-12s %.3f ms  (+%.3f)  rounds %s" % [p[0], t, med, med - base,
				str(v.map(func(x: float) -> String: return "%.3f" % x))])