class_name ShaderPrewarm extends RefCounted static var running := false static var last_report: Dictionary = {} static func counters() -> Dictionary: return {"mesh": int(Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_MESH)), "surface": int(Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_SURFACE)), "draw": int(Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_DRAW)), "specialization": int(Performance.get_monitor(Performance.PIPELINE_COMPILATIONS_SPECIALIZATION))} static func frames(game: Node, count: int = 3) -> void: for frame in count: await game.get_tree().process_frame await RenderingServer.frame_post_draw static func run(game: Node) -> void: if DisplayServer.get_name() == "headless" or running: return running = true var started := Time.get_ticks_msec() var before := counters() var overlay := CanvasLayer.new() overlay.layer = 120 var cover := FrontEnd.new() cover.loading = true overlay.add_child(cover) game.add_child(overlay) var status := cover.status var progress := cover.progress progress.max_value = Arsenal.DATA.size() * 2 + 5 var viewport := SubViewport.new() viewport.size = Vector2i(320, 240) viewport.own_world_3d = true viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS viewport.msaa_3d = game.get_viewport().msaa_3d game.add_child(viewport) var scene := Node3D.new() viewport.add_child(scene) var environment := WorldEnvironment.new() var arena: Node3D = game.get("arena") var actual := arena.get_node_or_null("DistrictAtmosphere") as WorldEnvironment environment.environment = actual.environment if actual != null else Environment.new() scene.add_child(environment) var camera := Camera3D.new() scene.add_child(camera) camera.position = Vector3(0, 1.5, 3) camera.look_at(Vector3(0, 1, 0)) camera.current = true var sun := DirectionalLight3D.new() sun.rotation_degrees = Vector3(-40, 30, 0) sun.shadow_enabled = true scene.add_child(sun) status.text = "Renderverfahren und Beleuchtung …" await frames(game) for index in Arsenal.DATA.size(): var weapon := WeaponView.new() scene.add_child(weapon) weapon.select_weapon(index) weapon.position.y = 1 weapon.muzzle.visible = true for attachments in 2: status.text = "Waffe %d/%d · %s" % [index + 1, Arsenal.DATA.size(), str(Arsenal.DATA[index].name)] if attachments == 1: WeaponSkins.apply(weapon.model, {"attachments": {"optic": 1, "muzzle": 1, "underbarrel": 1, "magazine": 1, "stock": 1}}) await frames(game) progress.value += 1 weapon.free() for variant: String in ["steve", "gas_mask"]: for arms in [false, true]: status.text = "Operator %s · %s" % [variant, "Ego-Arme" if arms else "Körperanimationen"] var actor := ImportedOperator.new() scene.add_child(actor) actor.setup(variant, arms) actor.preview("pro_rifle__reload", 0.4) await frames(game) progress.value += 1 actor.free() status.text = "Explosionen, Rauch, Treffer und Energieschild …" CombatVisuals.explosion(scene, Vector3.ZERO) CombatVisuals.impact(scene, Vector3(0, 1, 1)) CombatVisuals.flash(scene, Vector3(1, 1, 0)) for kind: String in ["flame", "embers", "smoke", "fireball", "flash"]: CombatVisuals.puff(scene, Vector3(0, 1, 0), 1.0, kind, 10, Vector3.ZERO) var shield := EnergyShield.new() scene.add_child(shield) shield.set_process(false) shield.visible = true shield.position.y = 1 await frames(game, 5) progress.value = progress.max_value status.text = "Bereit" await frames(game, 2) last_report = {"milliseconds": Time.get_ticks_msec() - started, "renderer": RenderingServer.get_current_rendering_method(), "msaa": int(viewport.msaa_3d), "stages": int(progress.max_value), "before": before, "after": counters()} var report := FileAccess.open("user://shader-prewarm.json", FileAccess.WRITE) if report != null: report.store_string(JSON.stringify(last_report, "\t")) print("SHADER PREWARM ", JSON.stringify(last_report)) viewport.queue_free() overlay.queue_free() running = false