diff --git a/include/core/application.hpp b/include/core/application.hpp index 35f5be0f..d54472af 100644 --- a/include/core/application.hpp +++ b/include/core/application.hpp @@ -154,6 +154,11 @@ private: ui::WidgetRenderer widgetRenderer_; /// The live head-and-shoulders view the interface's portrait draws. ui::UnitPortrait unitPortrait_; + /// Where the portrait's widget was found last time. Ids are stable, so + /// this saves a scan of every widget by name on every frame; the name is + /// still checked, because reloading the interface rebuilds the tree and + /// the id could then belong to something else. + uint32_t portraitWidgetId_ = 0; bool addonsLoaded_ = false; std::unique_ptr expansionRegistry_; // Empty means assets follow the active protocol profile. "legacy" selects diff --git a/src/core/application.cpp b/src/core/application.cpp index be49e11b..725cb837 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -2873,9 +2873,14 @@ void Application::render() { // descriptor set that has been destroyed — the render target is // torn down whenever the model is rebuilt — and drawing from // that is a use-after-free rather than a stale picture. - if (auto* w = engine->widgets().findByName("PlayerPortrait")) { - w->externalTexture = unitPortrait_.textureId(); + auto& widgets = engine->widgets(); + ui::Widget* portrait = portraitWidgetId_ + ? widgets.get(portraitWidgetId_) : nullptr; + if (!portrait || portrait->name != "PlayerPortrait") { + portrait = widgets.findByName("PlayerPortrait"); + portraitWidgetId_ = portrait ? portrait->id : 0; } + if (portrait) portrait->externalTexture = unitPortrait_.textureId(); } // Lay out first: hit testing reads the rects this produces, so