godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.22k stars 21.22k forks source link

Changing Label in FlowContainer with TextureRect will cause crash #93767

Closed CharlesWilsonBarbosa closed 3 months ago

CharlesWilsonBarbosa commented 4 months ago

Tested versions

Reproducible in: v4.2.1stable[b09f793f5]

System information

Godot v4.2.2.stable - Windows 10.0.22631 - Vulkan (Forward+) - integrated Intel(R) UHD Graphics 620 (Intel Corporation; 31.0.101.2125) - Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz (8 Threads)

Issue description

Using a Label in FlowContainer with changed constants will cause a crash in your Godot.

Steps to reproduce

2 ways now:

Wait and it will close (but if it doesn't close, change the FlowContainer or Label again)

Minimal reproduction project (MRP)

N/A

matheusmdx commented 4 months ago

I was able to reproduce the crash in latest master (https://github.com/godotengine/godot/commit/4ab8fb809396fa38ba929fec97cfcb7193f1c44d), when i type something in the label node godot freezes until the crash, when the crash happens vscode shows the printed error: Failed method: Control::_update_minimum_size. Message queue out of memory. Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_mb' in project settings. and a spam of: Object was deleted while awaiting a callback., here the backtrace:

Backtrace ``` abort() Line 77 (c:\Users\Matheus\AppData\Local\Programs\Microsoft VS Code\minkernel\crts\ucrt\src\appcrt\startup\abort.cpp:77) _purecall() Line 29 (d:\a\_work\1\s\src\vctools\crt\vcruntime\src\misc\purevirt.cpp:29) Callable::get_object() Line 164 (c:\Users\Matheus\Downloads\Godot Source\core\variant\callable.cpp:164) CallQueue::statistics() Line 370 (c:\Users\Matheus\Downloads\Godot Source\core\object\message_queue.cpp:370) CallQueue::push_callablep(const Callable & p_callable, const Variant * * p_args, int p_argcount, bool p_show_error) Line 99 (c:\Users\Matheus\Downloads\Godot Source\core\object\message_queue.cpp:99) Callable::call_deferredp(const Variant * * p_arguments, int p_argcount) Line 41 (c:\Users\Matheus\Downloads\Godot Source\core\variant\callable.cpp:41) Callable::call_deferred<>() Line 85 (c:\Users\Matheus\Downloads\Godot Source\core\variant\callable.h:85) Container::queue_sort() Line 139 (c:\Users\Matheus\Downloads\Godot Source\scene\gui\container.cpp:139) Container::_child_minsize_changed() Line 36 (c:\Users\Matheus\Downloads\Godot Source\scene\gui\container.cpp:36) call_with_variant_args_helper(Container * p_instance, void(Container::*)() p_method, const Variant * * p_args, Callable::CallError & r_error, IndexSequence<> __formal) Line 309 (c:\Users\Matheus\Downloads\Godot Source\core\variant\binder_common.h:309) call_with_variant_args(Container * p_instance, void(Container::*)() p_method, const Variant * * p_args, int p_argcount, Callable::CallError & r_error) Line 419 (c:\Users\Matheus\Downloads\Godot Source\core\variant\binder_common.h:419) CallableCustomMethodPointer::call(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 104 (c:\Users\Matheus\Downloads\Godot Source\core\object\callable_method_pointer.h:104) Callable::callp(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 58 (c:\Users\Matheus\Downloads\Godot Source\core\variant\callable.cpp:58) Object::emit_signalp(const StringName & p_name, const Variant * * p_args, int p_argcount) Line 1188 (c:\Users\Matheus\Downloads\Godot Source\core\object\object.cpp:1188) Node::emit_signalp(const StringName & p_name, const Variant * * p_args, int p_argcount) Line 3890 (c:\Users\Matheus\Downloads\Godot Source\scene\main\node.cpp:3890) Object::emit_signal<>(const StringName & p_name) Line 936 (c:\Users\Matheus\Downloads\Godot Source\core\object\object.h:936) Control::_update_minimum_size() Line 1610 (c:\Users\Matheus\Downloads\Godot Source\scene\gui\control.cpp:1610) call_with_variant_args_helper(Control * p_instance, void(Control::*)() p_method, const Variant * * p_args, Callable::CallError & r_error, IndexSequence<> __formal) Line 309 (c:\Users\Matheus\Downloads\Godot Source\core\variant\binder_common.h:309) call_with_variant_args(Control * p_instance, void(Control::*)() p_method, const Variant * * p_args, int p_argcount, Callable::CallError & r_error) Line 419 (c:\Users\Matheus\Downloads\Godot Source\core\variant\binder_common.h:419) CallableCustomMethodPointer::call(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 104 (c:\Users\Matheus\Downloads\Godot Source\core\object\callable_method_pointer.h:104) Callable::callp(const Variant * * p_arguments, int p_argcount, Variant & r_return_value, Callable::CallError & r_call_error) Line 58 (c:\Users\Matheus\Downloads\Godot Source\core\variant\callable.cpp:58) CallQueue::_call_function(const Callable & p_callable, const Variant * p_args, int p_argcount, bool p_show_error) Line 221 (c:\Users\Matheus\Downloads\Godot Source\core\object\message_queue.cpp:221) CallQueue::flush() Line 270 (c:\Users\Matheus\Downloads\Godot Source\core\object\message_queue.cpp:270) SceneTree::process(double p_time) Line 524 (c:\Users\Matheus\Downloads\Godot Source\scene\main\scene_tree.cpp:524) Main::iteration() Line 4099 (c:\Users\Matheus\Downloads\Godot Source\main\main.cpp:4099) OS_Windows::run() Line 1686 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\os_windows.cpp:1686) widechar_main(int argc, wchar_t * * argv) Line 181 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\godot_windows.cpp:181) _main() Line 206 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\godot_windows.cpp:206) main(int argc, char * * argv) Line 220 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\godot_windows.cpp:220) WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 234 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\godot_windows.cpp:234) [Inline Frame] invoke_main() Line 102 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:102) __scrt_common_main_seh() Line 288 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288) kernel32.dll!00007fffec3a7344() (Unknown Source:0) ntdll.dll!00007fffed3bcc91() (Unknown Source:0) ``` ![image](https://github.com/godotengine/godot/assets/44306054/0a11a843-60c1-4d13-8672-ed800b66cef4)


Increase message_queue size from 32 to 256 doesn't avoid the errors and the crash, just make the freeze last longer before the crash and give a slight different backtrace

Backtrace 2 ``` abort() Line 77 (c:\Users\Matheus\AppData\Local\Programs\Microsoft VS Code\minkernel\crts\ucrt\src\appcrt\startup\abort.cpp:77) _purecall() Line 29 (d:\a\_work\1\s\src\vctools\crt\vcruntime\src\misc\purevirt.cpp:29) Callable::get_object() Line 164 (c:\Users\Matheus\Downloads\Godot Source\core\variant\callable.cpp:164) CallQueue::statistics() Line 370 (c:\Users\Matheus\Downloads\Godot Source\core\object\message_queue.cpp:370) CallQueue::push_callablep(const Callable & p_callable, const Variant * * p_args, int p_argcount, bool p_show_error) Line 99 (c:\Users\Matheus\Downloads\Godot Source\core\object\message_queue.cpp:99) Callable::call_deferredp(const Variant * * p_arguments, int p_argcount) Line 41 (c:\Users\Matheus\Downloads\Godot Source\core\variant\callable.cpp:41) Callable::call_deferred<>() Line 85 (c:\Users\Matheus\Downloads\Godot Source\core\variant\callable.h:85) CanvasItem::queue_redraw() Line 433 (c:\Users\Matheus\Downloads\Godot Source\scene\main\canvas_item.cpp:433) RichTextLabel::_add_item(RichTextLabel::Item * p_item, bool p_enter, bool p_ensure_newline) Line 3122 (c:\Users\Matheus\Downloads\Godot Source\scene\gui\rich_text_label.cpp:3122) RichTextLabel::push_color(const Color & p_color) Line 3576 (c:\Users\Matheus\Downloads\Godot Source\scene\gui\rich_text_label.cpp:3576) EditorLog::_add_log_line(EditorLog::LogMessage & p_message, bool p_replace_previous) Line 363 (c:\Users\Matheus\Downloads\Godot Source\editor\editor_log.cpp:363) EditorLog::_process_message(const String & p_msg, EditorLog::MessageType p_type, bool p_clear) Line 242 (c:\Users\Matheus\Downloads\Godot Source\editor\editor_log.cpp:242) EditorLog::add_message(const String & p_msg, EditorLog::MessageType p_type) Line 259 (c:\Users\Matheus\Downloads\Godot Source\editor\editor_log.cpp:259) EditorLog::_error_handler(void * p_self, const char * p_func, const char * p_file, int p_line, const char * p_error, const char * p_errorexp, bool p_editor_notify, ErrorHandlerType p_type) Line 66 (c:\Users\Matheus\Downloads\Godot Source\editor\editor_log.cpp:66) _err_print_error(const char * p_function, const char * p_file, int p_line, const char * p_error, const char * p_message, bool p_editor_notify, ErrorHandlerType p_type) Line 98 (c:\Users\Matheus\Downloads\Godot Source\core\error\error_macros.cpp:98) SafeRefCount::_check_unref_safety() Line 187 (c:\Users\Matheus\Downloads\Godot Source\core\templates\safe_refcount.h:187) SafeRefCount::unref() Line 207 (c:\Users\Matheus\Downloads\Godot Source\core\templates\safe_refcount.h:207) Callable::~Callable() Line 430 (c:\Users\Matheus\Downloads\Godot Source\core\variant\callable.cpp:430) CallQueue::Message::~Message() (Unknown Source:0) CallQueue::Message::`scalar deleting destructor'(unsigned int) (Unknown Source:0) CallQueue::flush() Line 293 (c:\Users\Matheus\Downloads\Godot Source\core\object\message_queue.cpp:293) SceneTree::process(double p_time) Line 524 (c:\Users\Matheus\Downloads\Godot Source\scene\main\scene_tree.cpp:524) Main::iteration() Line 4099 (c:\Users\Matheus\Downloads\Godot Source\main\main.cpp:4099) OS_Windows::run() Line 1686 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\os_windows.cpp:1686) widechar_main(int argc, wchar_t * * argv) Line 181 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\godot_windows.cpp:181) _main() Line 206 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\godot_windows.cpp:206) main(int argc, char * * argv) Line 220 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\godot_windows.cpp:220) WinMain(HINSTANCE__ * hInstance, HINSTANCE__ * hPrevInstance, char * lpCmdLine, int nCmdShow) Line 234 (c:\Users\Matheus\Downloads\Godot Source\platform\windows\godot_windows.cpp:234) [Inline Frame] invoke_main() Line 102 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:102) __scrt_common_main_seh() Line 288 (d:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288) kernel32.dll!00007fffec3a7344() (Unknown Source:0) ntdll.dll!00007fffed3bcc91() (Unknown Source:0) ```
aaronp64 commented 4 months ago

Started looking into this. Having a hard time following the details of what's happening, but it seems to be an interaction between FlowContainer::_resort setting the TextureRect's size through fit_child_in_rect, and TextureRect handling NOTIFICATION_RESIZED by calling update_minimum_size. This leads to calling FlowContainer's _child_minsize_changed, queuing another _resort, leading to an infinite loop. The minimum size of both the TextureRect and FlowContainer alternate between two values on every other loop when using "Fit Width Proportional", maybe a consistent value would break the loop.

This seems like it might be related to https://github.com/godotengine/godot/issues/73071 and https://github.com/godotengine/godot/pull/73396. @KoBeWi, does this look like the same issue AspectRatioContainer had with TextureRect? If so, should we just add a similar check for TextureRect and the expand modes in FlowContainer::_resort?

KoBeWi commented 4 months ago

Yeah it's the same issue as #73071 Though it looks like it's partially supported, unlike AspectRatioContainer.

We can add similar check to FlowContainer, but I'd check if it's needed in every case.