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

Prevent infinite recursion in first _draw #97328

Merged
merged 1 commit into from
Dec 4, 2024

Conversation

AThousandShips
Copy link
Member

@AThousandShips AThousandShips commented Sep 22, 2024

Caused by size changes fired in some cases, mainly by auto-translation, and due to that _redraw_callback is called from a flush causing any queued methods to be fired immediately.

Added pending_update = false; to the _exit_canvas method to ensure things behave reasonably in some edge cases like changing the world_2d or reparenting self during _draw which would otherwise never fire a redraw. This is not really reasonable use but I feel it's good to not introduce weird behavior in these cases.

The underlying cause here seems to be that when entering the tree a bunch of different calls are made including several that unconditionally resize the node. These are likely inefficient and could be improved but this offers a fix that ensures things behave reasonably in these cases.

So I'd say this is a functional fix, we can look at reducing unnecessary calls to queue_redraw and resizing calls on entering the tree etc. (it happens in things like NOTIFY_TRANSLATION_CHANGED and a bunch of others) but this should ensure things run smoothly.

New fix, this fix avoids complex changes by deferring the translation notification, need confirmation that this won't break translation but in my testing it works with localization, and doesn't break.

MRP for testing:
recursive_draw.zip

@AThousandShips AThousandShips added bug topic:2d cherrypick:4.3 Considered for cherry-picking into a future 4.3.x release labels Sep 22, 2024
@AThousandShips AThousandShips added this to the 4.4 milestone Sep 22, 2024
@AThousandShips AThousandShips requested a review from a team as a code owner September 22, 2024 14:33
@AThousandShips
Copy link
Member Author

AThousandShips commented Sep 22, 2024

What seems to be happening here is that:

  • Node receives the NOTIFICATION_ENTER_TREE notification, fires the NOTIFICATION_TRANSLATION_CHANGED notification, which queues a draw (and size changes as well, which might or might not be relevant to that part)
  • CanvasItem receives the NOTIFICATION_ENTER_TREE notification, calls _enter_canvas which clears pending_update which forces a second draw
  • When the message queue is flushed the two draw calls fire back-to-back
  • The child node is added, this fires resizing in the child, which in turn fires resizing in the parent, when this happens in the second draw call pending_update is once again false as it was cleared at the end of the first draw, this allows a new draw to be queued immediately

If you remove the add_child call from the MRP and run it without this PR you will notice it draws twice, when it should only draw once, this removes that double call as well (which is the cause of the bug)

@AThousandShips
Copy link
Member Author

AThousandShips commented Sep 22, 2024

Unsure if this can be triggered in 4.2 but adding a cherry pick for it as the fix is limited in scope

Unsure if there's any weird behavior triggered by this same code in 3.x (it was added back before Godot was open source) but can make a dedicated fix for it if desired to eliminate any potential issues there too (don't have a 3.x setup to test myself right now)

@AThousandShips AThousandShips added the cherrypick:4.2 Considered for cherry-picking into a future 4.2.x release label Sep 22, 2024
@Mickeon
Copy link
Contributor

Mickeon commented Sep 22, 2024

The simplicity of this fix has me wonder if doing it this way has any unforeseen consequences.

@AThousandShips
Copy link
Member Author

I've dug through and tested various cases, the pending_update clearing in _enter_canvas only matters in the narrow edge case of when canvas operations happen during a draw call, as it will always be cleared otherwise, to be entirely safe (with the cost of losing the case of changing the world or re-parenting the node during a draw call) I could remove it in _exit_canvas, but there simply isn't any need for the pending_update clearing in _enter_canvas and it becoming a problem was really only revealed by the translation changes, it was always a risk just not caused by any existing condition (that I know of, there could be cases that cause this in older versions just haven't found any)

@syntaxerror247
Copy link
Member

What is current status of this PR ?

@AThousandShips
Copy link
Member Author

It's awaiting review, it works well but needs some decision from the core team

@clayjohn
Copy link
Member

The PR itself makes sense to me. But I am a little skeptical seeing as the behaviour changed between 4.2 and 4.3, but the code this PR changes hasn't been touched in many years.

I suspect that it would be best to solve this bug in the spot that introduced the regression otherwise we will be risking breaking other untested code paths in subtle ways

@AThousandShips
Copy link
Member Author

I agree, will look at pinpointing what changed and see if any changes to this fix are needed

@AThousandShips AThousandShips removed the cherrypick:4.2 Considered for cherry-picking into a future 4.2.x release label Nov 21, 2024
@AThousandShips
Copy link
Member Author

I have an alternative solution and will push momentarily, will keep the current fix for a potential follow-up fix if we can confirm the changes are safe to future proof this code

@AThousandShips
Copy link
Member Author

AThousandShips commented Nov 22, 2024

Re-confirming this still fixes the linked bugs but this should be a conservative fix still resolving the problem

Edit: Confirmed, they all now work correctly

@clayjohn
Copy link
Member

Great work tracking that down! This indeed seems like a much safer change to make. Pinging @YeldhamDev to confirm that the translation stuff still looks good

@Repiteo Repiteo merged commit 610f4a2 into godotengine:master Dec 4, 2024
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Dec 4, 2024

Thanks!

@AThousandShips AThousandShips deleted the fix_recursive_draw branch December 4, 2024 18:31
@AThousandShips
Copy link
Member Author

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug cherrypick:4.3 Considered for cherry-picking into a future 4.3.x release topic:gui
Projects
None yet
6 participants