From eb5ad19dbc488345163f0c076911fb38368cf115 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Tue, 29 Sep 2026 22:44:36 -0600 Subject: [PATCH] Update aurora & utilize RmlUi culling Huge improvement to Mod Manager performance in particular --- extern/aurora | 2 +- res/rml/mods.rcss | 1 + res/rml/window.rcss | 1 + src/dusk/ui/list.cpp | 77 ++++++++++++++++++-------------------------- src/dusk/ui/list.hpp | 9 +++--- 5 files changed, 40 insertions(+), 50 deletions(-) diff --git a/extern/aurora b/extern/aurora index bded88e95d..3227d76c60 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit bded88e95d5a1a7c8b2d747843cb8cde17b39838 +Subproject commit 3227d76c60e1e782ca576610bce61c9e7744d8be diff --git a/res/rml/mods.rcss b/res/rml/mods.rcss index daaf1c90b9..b13b826591 100644 --- a/res/rml/mods.rcss +++ b/res/rml/mods.rcss @@ -59,6 +59,7 @@ mod-entry { transition: decorator 0.1s linear-in-out; cursor: pointer; focus: auto; + contain: paint; } mod-entry.current { diff --git a/res/rml/window.rcss b/res/rml/window.rcss index 130d4bb1ec..9a007c3ade 100644 --- a/res/rml/window.rcss +++ b/res/rml/window.rcss @@ -147,6 +147,7 @@ ui-list-content { ui-list-content > button.ui-list-row { flex: 0 0 auto; + contain: paint; } ui-list-empty { diff --git a/src/dusk/ui/list.cpp b/src/dusk/ui/list.cpp index fc8d233847..6f603006a8 100644 --- a/src/dusk/ui/list.cpp +++ b/src/dusk/ui/list.cpp @@ -32,7 +32,7 @@ List::List(Rml::Element* parent, Props props) mEmpty = append_element(mRoot, "ui-list-empty"); append_text(mEmpty, "No items"); - Component::listen(mViewport, Rml::EventId::Scroll, [this](Rml::Event&) { mCullDirty = true; }); + Component::listen(mViewport, Rml::EventId::Scroll, [this](Rml::Event&) { mRangeDirty = true; }); listen(Rml::EventId::Keydown, [this](Rml::Event& event) { handle_keydown(event); }); apply_items(std::move(mProps.items)); @@ -52,19 +52,13 @@ void List::update() { apply_items(std::move(items), snapshotFocus); } - for (const auto& row : mRows) { - if (!row->culled) { - row->button->update(); - } - } - const float scrollTop = mViewport->GetScrollTop(); const float viewportWidth = mViewport->GetClientWidth(); const float viewportHeight = mViewport->GetClientHeight(); if (scrollTop != mLastScrollTop || viewportWidth != mLastViewportWidth || viewportHeight != mLastViewportHeight) { - mCullDirty = true; + mRangeDirty = true; if (viewportWidth != mLastViewportWidth || viewportHeight != mLastViewportHeight) { mLayoutScanFrames = std::max(mLayoutScanFrames, 2); } @@ -73,14 +67,25 @@ void List::update() { mLastViewportHeight = viewportHeight; } - if (mCullDirty || mLayoutScanFrames > 0) { - update_culling(); - mCullDirty = false; + if (mRangeDirty || mLayoutScanFrames > 0) { + update_active_range(); + mRangeDirty = false; if (mLayoutScanFrames > 0) { --mLayoutScanFrames; } } + if (mUpdateAllRows) { + mUpdateAllRows = false; + for (const auto& row : mRows) { + row->button->update(); + } + } else { + for (size_t i = mActiveFirst; i < std::min(mActiveLast, mRows.size()); ++i) { + mRows[i]->button->update(); + } + } + update_pending_focus(); } @@ -156,7 +161,6 @@ std::unique_ptr List::create_row(const Item& item) { [this, key = item.key] { return mProps.isDisabled && mProps.isDisabled(key); }, }); row->button->root()->SetClass("ui-list-row", true); - row->button->root()->SetProperty("visibility", "hidden"); row->button->Component::listen(row->button->root(), Rml::EventId::Focus, [this, key = item.key](Rml::Event&) { mActiveKey = key; }); row->button->on_pressed([this, key = item.key] { @@ -230,43 +234,29 @@ void List::apply_items(std::vector items, const std::optionalGetAbsoluteOffset(Rml::BoxArea::Border).y; +void List::update_active_range() { + mActiveFirst = 0; + mActiveLast = 0; const float viewHeight = mViewport->GetClientHeight(); if (viewHeight <= 0.0f) { return; } - auto* context = mRoot->GetContext(); - const Row* focusedRow = context != nullptr ? row_from_element(context->GetFocusElement()) : nullptr; - - for (const auto& row : mRows) { - auto* element = row->button->root(); - const float top = element->GetAbsoluteOffset(Rml::BoxArea::Border).y - viewTop; - const bool inWindow = - top + element->GetOffsetHeight() >= -viewHeight && top <= viewHeight * 2.0f; - const bool focusGuard = - row.get() == focusedRow || (mPendingFocusKey && row->key == *mPendingFocusKey); - const bool shouldShow = inWindow || focusGuard; - if (shouldShow && row->culled) { - show_row(*row); - } else if (!shouldShow && !row->culled) { - row->culled = true; - element->SetProperty("visibility", "hidden"); - } - } -} - -void List::show_row(Row& row) { - if (!row.culled) { - return; - } - row.button->update(); - row.button->root()->RemoveProperty("visibility"); - row.culled = false; + const float viewTop = mViewport->GetAbsoluteOffset(Rml::BoxArea::Border).y; + const auto rowTop = [](const std::unique_ptr& row) { + return row->button->root()->GetAbsoluteOffset(Rml::BoxArea::Border).y; + }; + const auto first = std::ranges::partition_point(mRows, [&](const auto& row) { + return rowTop(row) + row->button->root()->GetOffsetHeight() < viewTop - viewHeight; + }); + const auto last = std::ranges::partition_point(first, mRows.end(), + [&](const auto& row) { return rowTop(row) <= viewTop + viewHeight * 2.0f; }); + mActiveFirst = static_cast(first - mRows.begin()); + mActiveLast = static_cast(last - mRows.begin()); } bool List::focus_row(int index, bool mayEnterList) { @@ -274,7 +264,6 @@ bool List::focus_row(int index, bool mayEnterList) { return false; } auto& row = *mRows[index]; - show_row(row); row.button->update(); if (row.button->root()->IsPseudoClassSet("disabled")) { return false; @@ -295,7 +284,6 @@ void List::request_focus(uint64_t key, bool mayEnterList) { if (it == mRowsByKey.end()) { return; } - show_row(*it->second); mPendingFocusKey = key; mPendingFocusFrames = 2; mPendingFocusMayEnterList = mayEnterList; @@ -329,7 +317,6 @@ void List::update_pending_focus() { } auto& row = *it->second; - show_row(row); row.button->update(); if (!row.button->root()->IsPseudoClassSet("disabled") && row.button->focus()) { mActiveKey = row.key; diff --git a/src/dusk/ui/list.hpp b/src/dusk/ui/list.hpp index 11a9dab5d6..e1ad9c2d65 100644 --- a/src/dusk/ui/list.hpp +++ b/src/dusk/ui/list.hpp @@ -42,7 +42,6 @@ private: struct Row { uint64_t key = 0; std::unique_ptr button; - bool culled = true; }; Row* row_from_element(Rml::Element* element) const; @@ -50,8 +49,7 @@ private: SnapshotFocus capture_snapshot_focus(); std::unique_ptr create_row(const Item& item); void apply_items(std::vector items, const std::optional& snapshotFocus = {}); - void update_culling(); - void show_row(Row& row); + void update_active_range(); bool focus_row(int index, bool mayEnterList); void request_focus(uint64_t key, bool mayEnterList); void update_pending_focus(); @@ -70,7 +68,10 @@ private: std::optional mPendingFocusKey; int mPendingFocusFrames = 0; bool mPendingFocusMayEnterList = false; - bool mCullDirty = true; + size_t mActiveFirst = 0; + size_t mActiveLast = 0; + bool mRangeDirty = true; + bool mUpdateAllRows = true; int mLayoutScanFrames = 2; float mLastScrollTop = -1.0f; float mLastViewportWidth = -1.0f;