Optimize UI rendering hot paths and reduce runtime allocations#44
Open
DeusSixik wants to merge 2 commits into
Open
Optimize UI rendering hot paths and reduce runtime allocations#44DeusSixik wants to merge 2 commits into
DeusSixik wants to merge 2 commits into
Conversation
Author
|
Quick question regarding List.copyOf(children) in Instead of creating a new List, I use |
Member
|
it looks pretty good. I agree most of changes, I will make few adjustments before merge |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Performance and Runtime Cleanup for UI Rendering and Utility Paths
This pull request focuses on reducing allocation pressure in hot paths, simplifying several runtime-heavy code paths, and replacing outdated or suboptimal data structures where the usage pattern is clearly single-threaded.
The changes are intentionally conservative where rendering behavior is involved: whenever possible, the optimizations preserve the existing external behavior and visual output while reducing per-frame overhead.
Summary
ArrayListandStackusages withObjectArrayListwhere the code is single-threaded and iteration-heavy.RectTexture.com.lowdragmc.lowdraglib2.test.ui.TestRenderOperation.List.copyOf(...)would only add unnecessary copying.Changes by Component
com.lowdragmc.lowdraglib2.gui.ui.ModularUIelementsstorage was migrated toObjectArrayList.getAllElements()now returnsObjectLists.unmodifiable(...)instead ofCollections.unmodifiableList(...), preserving a more efficientObjectList-backed view.com.lowdragmc.lowdraglib2.gui.ui.UIElementchildren,styles, andlocalStylesheetscollections were migrated toObjectArrayList.sortedChildrenCachestrategy was reworked to reduce GC pressure during child sorting.int[] indexArrayBufferandboolean isSortedCacheDirtyto support the new sorting/cache flow.getSortedChildren()was rewritten to reduce allocations during repeated sorting.clearSortedChildrenCache()now clears and invalidates the cache without relying onnullreplacement.clearLocalStylesheets()was changed to avoid creating unnecessary temporary lists.com.lowdragmc.lowdraglib2.gui.ui.event.UIEventDispatcherdrillDown()no longer relies on iterator-based traversal for sorted children and instead iterates more directly over the cached result.com.lowdragmc.lowdraglib2.gui.ui.rendering.GUIContextjava.util.StackwithObjectArrayList.postRenderingCallstoObjectArrayList.com.lowdragmc.lowdraglib2.gui.ui.rendering.UIVisualLayerTARGET_POOLandMASK_POOLqueue-based storage withObjectArrayListusing LIFO reuse.ensureTargetValid()andensureMaskValid()to reuse the most recently released buffers first.target == nullearly-return path insidedraw()to avoid invalid access if the layer is used incorrectly.com.lowdragmc.lowdraglib2.gui.ui.elements.GraphViewrefreshContentTransform()visibility fromprivatetoprotectedso subclasses can cooperate with layout updates safely.com.lowdragmc.lowdraglib2.gui.ui.utils.HistoryStackundoStackandredoStackfromjava.util.StacktoObjectArrayList.push,pop, andpeekto keep the call sites readable without depending onStack.checkStackSize()to useremoveElements(...)instead ofsubList(...).clear(), which better matchesObjectArrayList.com.lowdragmc.lowdraglib2.editor.ui.view.HistoryViewundoStackandredoStackfromjava.util.StacktoObjectArrayList.HistoryStackthrough helper-style stack operations.checkStackSize()to remove old entries without relying onsubList(...).clear().com.lowdragmc.lowdraglib2.gui.util.TreeBuilderstackfromjava.util.StacktoObjectArrayList.com.lowdragmc.lowdraglib2.gui.ui.elements.UITemplateElementjava.util.StacktoObjectArrayList.com.lowdragmc.lowdraglib2.gui.ui.elements.TreeListexpandNodeAlongPath()fromjava.util.StacktoObjectArrayList.com.lowdragmc.lowdraglib2.nodegraphtookit.model.ChangeHintListgetHints()now keeps a cachedCollections.unmodifiableList(changeHints)view instead of creating fresh wrappers/copies for repeated reads.com.lowdragmc.lowdraglib2.nodegraphtookit.model.GraphElementModelgetCapabilities()now keeps a cachedCollections.unmodifiableList(capabilities)view instead of recreating wrappers or copies on every call.com.lowdragmc.lowdraglib2.editor.resource.ResourceInstancelistAllResources()intentionally keepsCollections.unmodifiableList(resources)because the method already builds a temporary list, and replacing it withList.copyOf(...)would only add another unnecessary allocation.com.lowdragmc.lowdraglib2.gui.texture.RectTextureList<Vector2f>[]to a flatfloat[]layout.Vector4f radiusnow follows the expected order:x = left-topy = right-topz = right-bottomw = left-bottomdrawFill()anddrawBorder()by extractinghalfWidth,halfHeight, and a sharedmaxRadius.com.lowdragmc.lowdraglib2.test.ui.TestRenderOperationLT/RT/RB/LBorder for faster debugging.com.lowdragmc.lowdraglib2.client.utils.RenderBufferUtilsdrawColorLines()anddrawColorTexLines()to reduce per-frame overhead:RandomAccessfast path,Vector3f perpobjects with localpx/py,emittedAnyflag instead of relying on the first index only.drawLine(),drawLines(), anddrawEdges():Vector3fcreation,drawLine(...)overloads that operate directly on rawfloatcoordinates.Vector3foverloads as thin wrappers around the float-based implementation.drawCircleLine()so it no longer allocates temporaryprevPoint,firstPoint, orcurrentPointvectors and instead uses local float coordinates plus the new float-baseddrawLine(...).shapeCube()andshapeSphere()by removing temporaryfloat[][],int[][], and short-livedfloat[]allocations in geometry generation loops.drawCubeFace(),renderCubeFace(),shapeCone(), andshapeCircle()unchanged because they do not show the same level of avoidable allocation overhead and are more sensitive to accidental geometry regressions.Implementation Notes
Why
ObjectArrayListObjectArrayListwas preferred overArrayListin several hotspots because:Why
Stackwas removedjava.util.Stackis both legacy and synchronized. In all updated call sites here, the usage pattern is clearly local or single-threaded, so the synchronization overhead is unnecessary. If FastUtil had not already been present,ArrayDequewould have been the most natural standard-library replacement.Why some read-only lists were not changed to
List.copyOf(...)Not every
Collections.unmodifiableList(...)should be replaced mechanically. In cases such asResourceInstance, an additionalList.copyOf(...)would create a fresh copy of already-temporary data and provide no practical benefit.Validation
com.lowdragmc.lowdraglib2.testand on custom example-based test screens created for this work.Profiling Evidence
Expected Outcome
This PR should reduce allocation pressure in UI rendering and utility code, improve data locality in several hot paths, remove outdated stack usage, and fix one visible
RectTexturerendering bug, while keeping the overall behavior and public usage patterns stable.This refactor was driven by practical observations made during mod development. There are still additional places in the UI codebase that may be worth reviewing in the future, but for now they do not appear critical enough to justify expanding the scope of this PR.