extends SceneTree
## GPU cost of evaluating the REAL density function per fragment.
##
## Sweeps the pixel footprint -- which is what nyquist_octaves turns into an
## octave count -- and reports GPU ms against a trivial baseline.
##
## ⚠ MEASURED WITH viewport_get_measured_render_time_gpu, NOT wall time.
## Two earlier versions of this bench timed _process with Time.get_ticks_usec
## and reported 6.900 ms for EVERY step including the trivial baseline -- 144 Hz
## vsync, to three decimals, across a 1000x range of octave load. Wall time on
## the CPU cannot see an async GPU. This asks the renderer.
##
## ⚠ THE SANITY STEP IS LOad-BEARING. Step 0 forces a 0.001 m footprint, i.e.
## the maximum octave count. If that does not cost visibly more than baseline,
## the quad is not being rasterised and every other number here is noise.
##
## DISPLAY=:0 "$HYPER_TPS_GODOT" --path . --rendering-driver vulkan \
## --script res://scenes/test/density_bench.gd
var _mat: ShaderMaterial
var _base: ShaderMaterial
var _mi: MeshInstance3D
var _vp: RID
# The probe has exactly two paths: 1 tap (analytic) and 4 taps (central
# differences in x and z only -- dd/dy is analytically exact). An earlier
# revision of this list said 6 and the shader ran 4, so the label lied.
var _steps: Array = [32.0, 32.0]
var _taps: Array = [1, 4]
var _i := -1
var _frame := 0
var _acc := 0.0
const WARM := 30
const MEASURE := 90
var _results: Array = []
func _initialize() -> void:
var q := QuadMesh.new()
q.size = Vector2(4, 4)
_mi = MeshInstance3D.new()
_mi.mesh = q
_mi.extra_cull_margin = 1e5
var base_sh := Shader.new()
base_sh.code = "shader_type spatial;\nrender_mode unshaded;\nvoid fragment(){ALBEDO=vec3(0.5);}"
_base = ShaderMaterial.new(); _base.shader = base_sh
_mat = ShaderMaterial.new(); _mat.shader = load("res://shaders/density_probe.gdshader")
_mi.material_override = _base
root.add_child(_mi)
var cam := Camera3D.new()
cam.position = Vector3(0, 0, 1.9) # 4x4 quad overfills a 70-deg fov
cam.current = true
root.add_child(cam)
var w := root.get_window()
if w:
w.size = Vector2i(1920, 1080)
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
Engine.max_fps = 0
_vp = root.get_viewport_rid()
RenderingServer.viewport_set_measure_render_time(_vp, true)
print("BENCH: 1920x1080 quad, GPU-timed, %d frames/step" % MEASURE)
func _process(_d: float) -> bool:
_frame += 1
if _frame > WARM:
_acc += RenderingServer.viewport_get_measured_render_time_gpu(_vp)
if _frame < WARM + MEASURE:
return false
var ms := _acc / float(MEASURE)
if _i < 0:
print(" baseline (trivial shader) : %6.3f ms GPU" % ms)
else:
print(" footprint %8.3f m taps=%d : %6.3f ms GPU" % [_steps[_i], _taps[_i], ms])
_results.append(ms)
_i += 1; _frame = 0; _acc = 0.0
if _i >= _steps.size():
_report(); quit(0); return true
_mi.material_override = _mat
_mat.set_shader_parameter("force_voxel_scale", _steps[_i])
_mat.set_shader_parameter("probe_taps", _taps[_i])
return false
func _report() -> void:
var base: float = _results[0]
var worst: float = _results[1] - base
print("BENCH: cost above baseline -- 6 density evals/fragment over 2.07M px")
for k in range(1, _results.size()):
print(" %8.3f m taps=%d : +%7.3f ms" % [_steps[k - 1], _taps[k - 1], _results[k] - base])
if worst < 0.05:
print("BENCH: FAIL — max octave load costs %.3f ms over baseline." % worst)
print(" The quad is almost certainly not rasterised. Numbers are void.")
else:
print("BENCH: sanity ok — max octave load costs %.3f ms, shader is running." % worst)