Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize DLabel autostretch behavior #2089

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

thecraftianman
Copy link

@thecraftianman thecraftianman commented Jun 24, 2024

Currently, DLabels will constantly try to perform their vertical autostretch behavior on Think, which has a notable performance impact when many panels derived from it are present on screen (such as in a DListView). This PR simply moves this behavior into PerformLayout, forcing these panels to attempt autostretch only when they may actually need to do so.

This also modifies Panel:SizeToContentsY and Panel:SizeToContentsX behavior to cache their last respective contents sizes used in order to avoid constant PerformLayout loops.

Autostretch logic before (measured over 20 seconds):
image

Autostretch logic after (measured over the same time period):
image

Also modifies Panel:SizeToContentsY and Panel:SizeToContentsX behavior to cache the last contents size used to avoid constant PerformLayout loops

self:SetTall( h + ( addval or 0 ) )
local newSize = h + ( addVal or 0 )
if ( newSize == self:GetTall() ) then return end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these checks actually do anything? They are also performed on the C side.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally added those checks to avoid constant PerformLayouts with certain DLabel panels that have autostretch enabled and thought that I could remove the variable to make the changes a bit simpler, but it seems like this change causes the same issue whereas using a variable for caching does not.

Here's an example that I'm using to test this behavior with vgui_visualizelayout 1:

    local testFrame = vgui.Create("DFrame")
    testFrame:SetSize(400, 400)
    testFrame:SetDeleteOnClose(true)
    local testLabel = vgui.Create("DLabel", testFrame)
    testLabel:Dock(FILL)
    testLabel:SetText("test")
    testLabel:SetAutoStretchVertical(true)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't the proposed changes just make the function not work if the label's size gets changing by whatever means?

For example:

local testFrame = vgui.Create("DFrame")
testFrame:SetSize(400, 400)
testFrame:SetDeleteOnClose(true)
testFrame:MakePopup()

local testLabel = vgui.Create("DLabel", testFrame)
testLabel:Dock(FILL)
testLabel:SetText("test")
print( "Size 1: ", testLabel:GetTall())
testLabel:SizeToContentsY()
print( "Size 2: ", testLabel:GetTall())
testLabel:SetTall( 60 )
print( "Size 3: ", testLabel:GetTall())
testLabel:SizeToContentsY()
print( "Size 4: ", testLabel:GetTall()) -- Not sized to contents!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I believe I've come up with a solution that should both avoid the broken resizing you've shown and prevent any potential infinite layout loops in 1648df2.

@robotboy655 robotboy655 added the Enhancement The pull request enhances current functionality. label Jul 9, 2024
@robotboy655
Copy link
Collaborator

I have tested the new code, it works better, but it does introduce difference in behavior.

image

I am not sure what's the preferred outcome is with Dock( FILL )ing a self-sizing panel is, it is kind of a paradox.

But you mention that this was originally aimed at fixing performance issues with DListView, can you provide an example where this issue can be observed?

@thecraftianman
Copy link
Author

The behavior change with Dock(FILL) was intentional; I wasn't really sure what the intended outcome should be either because the current behavior just formats the panel in the same way as Dock(LEFT), so I just tried to go with the one that doesn't get stuck infinitely refreshing the panel. If you'd prefer the behavior to stay the same, though, I'm happy to make that change for you. I need to make one more tweak to this PR anyways because I've found an issue with another panel in the meantime.

For an example with a slow DListView, just run FProfiler in Client mode. All of the DLabel Thinks in its own DListView will consistently be one of the top bottlenecks.

@thecraftianman
Copy link
Author

This latest commit should both prevent any infinite layouts with affected Dock types and avoid any visual changes to existing panels, as shown here:
image

@robotboy655
Copy link
Collaborator

I am not sure I like the fact that you are disabling the Dock on the panel from the panel itself.

It may work for your example and I can understand what you are trying to do here, but I am afraid it may cause issues when people are doing things like re-parenting the panel, and expecting the dock state to persist.

I don't think there's a way to preserve existing behavior while making it be not called as many times.

I think the best approach would be to just don't set the height for certain dock types, without resetting the docktype. This has potential to break existing addons still, due to the change in visual behavior, but this alignment issue should be solved via Panel:SetContentAlignment, not by relying on weird behavior like this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Enhancement The pull request enhances current functionality.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants