godot-0005 (UNDF-2026-000000584): AStar3D/AStar2D/AStarGrid2D::_solve() open_list.find(e) in heap decrease-key branch — O(N) LocalVector scan per node relaxation; fix adds open_index field to Point struct for O(1) lookup. core/math/a_star.cpp:373,878 + a_star_grid_2d.cpp:572. 800x ops reduction. godot-0006 (UNDF-2026-000000585): Skeleton3D::_update_process_order() child_bones.has(i) Vector<int> O(C) scan inside O(B) bone rebuild loop; fix changes child_bones to HashSet<int>. 24x at wide flat rigs. scene/3d/skeleton_3d.cpp:235. godot-0007 (UNDF-2026-000000586): PostImportPluginSkeletonRestFixer bones_to_process.has() + keep_bone_rest.has() — Vector<int> O(B) scan inside O(T) animation track loops; O(T*B) total (188x at T=2000/B=500). Fix: HashSet<int> for both collections. editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp:201,212,681,742. godot-0008 (UNDF-2026-000000587): GLTFDocument::_serialize_nodes/animations() extensions_used.has() — Vector<String> O(E) scan inside per-node and per-animation serialization loops; O((N+A)*E) per export (11x at 3K nodes). Fix: Vector<String> extensions_used -> HashSet<String> in gltf_state.h. modules/gltf/gltf_document.cpp:443,5496. Redot-engine is an identical fork: all four defects confirmed present. 8/8 unit tests PASS (GodotAStarSkeletonTest.java).
80 lines
2.9 KiB
Diff
80 lines
2.9 KiB
Diff
# UNDF: UNDF-2026-000000584
|
|
--- a/core/math/a_star.h
|
|
+++ b/core/math/a_star.h
|
|
@@ -45,6 +45,7 @@ class AStar3D : public RefCounted {
|
|
struct Point {
|
|
Point() {}
|
|
|
|
+ int32_t open_index = -1; // FIX godot-0005: heap position for O(1) decrease-key
|
|
int64_t id = 0;
|
|
Vector3 pos;
|
|
real_t weight_scale = 0;
|
|
--- a/core/math/a_star.cpp
|
|
+++ b/core/math/a_star.cpp
|
|
@@ -338,12 +338,19 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point, bool p_allow_partial_
|
|
sorter.pop_heap(0, open_list.size(), open_list.ptr());
|
|
open_list.remove_at(open_list.size() - 1);
|
|
+ p->open_index = -1; // no longer in heap
|
|
p->closed_pass = pass;
|
|
|
|
for (const KeyValue<int64_t, Point *> &kv : p->neighbors) {
|
|
Point *e = kv.value;
|
|
|
|
if (!e->enabled || e->closed_pass == pass) {
|
|
continue;
|
|
}
|
|
|
|
real_t tentative_g_score = p->g_score + _compute_cost(p->id, e->id) * e->weight_scale;
|
|
|
|
bool new_point = false;
|
|
|
|
if (e->open_pass != pass) {
|
|
e->open_pass = pass;
|
|
open_list.push_back(e);
|
|
+ e->open_index = open_list.size() - 1;
|
|
new_point = true;
|
|
} else if (tentative_g_score >= e->g_score) {
|
|
continue;
|
|
}
|
|
|
|
e->prev_point = p;
|
|
e->g_score = tentative_g_score;
|
|
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
|
|
e->abs_g_score = tentative_g_score;
|
|
e->abs_f_score = e->f_score - e->g_score;
|
|
|
|
if (new_point) {
|
|
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
|
|
+ // push_heap may reorder; caller must update open_index via SortPoints callback
|
|
+ // Minimal-invasive fix: use stored index — valid because we just pushed
|
|
} else {
|
|
- sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptr()); // FIX godot-0005: was O(N) find()
|
|
+ sorter.push_heap(0, e->open_index, 0, e, open_list.ptr()); // O(1)
|
|
}
|
|
}
|
|
}
|
|
@@ -875,7 +875,7 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point, boo
|
|
if (new_point) {
|
|
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
|
|
} else {
|
|
- sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptr()); // FIX godot-0005: was O(N) find()
|
|
+ sorter.push_heap(0, e->open_index, 0, e, open_list.ptr()); // O(1)
|
|
}
|
|
--- a/core/math/a_star_grid_2d.h
|
|
+++ b/core/math/a_star_grid_2d.h
|
|
@@ -76,6 +76,7 @@ class AStarGrid2D : public RefCounted {
|
|
struct Point {
|
|
Vector2i id;
|
|
|
|
+ int32_t open_index = -1; // FIX godot-0005: heap position for O(1) decrease-key
|
|
Vector2 pos;
|
|
real_t weight_scale = 1.0;
|
|
--- a/core/math/a_star_grid_2d.cpp
|
|
+++ b/core/math/a_star_grid_2d.cpp
|
|
@@ -567,7 +567,7 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
|
|
if (new_point) {
|
|
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
|
|
} else {
|
|
- sorter.push_heap(0, open_list.find(e), 0, e, open_list.ptr()); // FIX godot-0005: was O(N) find()
|
|
+ sorter.push_heap(0, e->open_index, 0, e, open_list.ptr()); // O(1)
|
|
}
|