fix(M00): release GUI capture render resources

Work-Package: M00-QAR-GUI-RID-SHUTDOWN-001
Agent: sindo-main-codex
Tests: render baseline dry-run; runtime cache shutdown; comparator self-test; coordination and documentation gates
Fidelity: shutdown-only ownership fix; no visual or Blizzlike335 behavior change
This commit is contained in:
2026-07-13 11:08:30 +04:00
parent d1df794cd6
commit 09b3e87a8d
8 changed files with 231 additions and 13 deletions
+52 -7
View File
@@ -374,6 +374,39 @@ func _exit_tree() -> void:
_shutting_down = true
_wait_for_tile_tasks()
_clear_streamed_world()
_release_runtime_caches_for_shutdown()
## Frees detached prototype nodes and releases render resources owned by this
## loader after all asynchronous work has completed. Map refreshes retain these
## caches; only scene shutdown destroys them.
func _release_runtime_caches_for_shutdown() -> void:
_free_detached_node_cache(_m2_scene_cache)
_free_detached_node_cache(_m2_animated_scene_cache)
_free_detached_node_cache(_wmo_prototype_cache)
_m2_mesh_cache.clear()
_m2_static_animation_cache.clear()
_m2_missing_cache.clear()
_wmo_render_cache.clear()
_wmo_scene_resource_cache.clear()
_wmo_render_missing_cache.clear()
_wmo_scene_cache_missing.clear()
_wmo_missing_cache.clear()
_shared_tex_cache.clear()
func _free_detached_node_cache(node_cache: Dictionary) -> void:
for cached_value in node_cache.values():
if not (cached_value is Node):
continue
var cached_node := cached_value as Node
if not is_instance_valid(cached_node):
continue
if cached_node.is_inside_tree():
cached_node.queue_free()
else:
cached_node.free()
node_cache.clear()
func _apply_quality_preset() -> void:
@@ -2781,15 +2814,27 @@ func _clear_streamed_world() -> void:
_free_render_instance(chunk_rid)
var tile_root: Node = state.get("root", null)
if tile_root:
tile_root.queue_free()
_queue_free_streamed_node(tile_root)
_tile_states.clear()
_clear_tile_mesh_cache()
_clear_quality_terrain_mesh_cache()
_clear_wmo_registry()
_ensure_world_wmo_root()
if enable_wmo:
_place_global_wmo()
if not _shutting_down:
_ensure_world_wmo_root()
if enable_wmo:
_place_global_wmo()
## During world shutdown, parented nodes are recursively owned by the world
## root. Queueing them separately from `_exit_tree()` can detach them from that
## deletion and leave empty Node objects alive at engine teardown.
func _queue_free_streamed_node(node: Node) -> void:
if node == null or not is_instance_valid(node):
return
if _shutting_down and node.is_inside_tree():
return
node.queue_free()
func _reset_terrain_root_children() -> void:
@@ -3889,10 +3934,10 @@ func _clear_wmo_registry() -> void:
var entry: Dictionary = entry_variant
var node: Node = entry.get("node", null)
if node and is_instance_valid(node):
node.queue_free()
_queue_free_streamed_node(node)
_wmo_registry.clear()
if _world_wmo_root and is_instance_valid(_world_wmo_root):
_world_wmo_root.queue_free()
_queue_free_streamed_node(_world_wmo_root)
_world_wmo_root = null
@@ -4458,7 +4503,7 @@ func _cancel_m2_build_job(key: String) -> void:
var job: Dictionary = _m2_build_jobs[key]
var root: Node = job.get("root", null)
if root != null and is_instance_valid(root):
root.queue_free()
_queue_free_streamed_node(root)
_m2_build_jobs.erase(key)