godot
godot copied to clipboard
Calling play() on AudioStreamPlayer2D causes crash without error log when World2D is shared with SubViewport and scene is reloaded.
Tested versions
v4.2.1.stable.official [b09f793f5]
System information
Godot v4.2.1.stable - Windows 10.0.19045 - Vulkan (Forward+)
Issue description
Hello,
I've been experiencing an issue where Godot crashes when play() is called if a SubViewport's World2D is set to the scene root's World2D, after the scene is reloaded. More specifically, the crash doesn't happen consistently, but rather sometimes it crashes instantly, and other times it crashes after a few reloads.
Some things I've observed:
- Occasionally upon scene reload the debugger will throw the following error: "affine_invert: Condition "det == 0" is true." (Can't find the offending transform)
- This isn’t observed when World2D isn’t shared with the SubViewport.
- This isn’t observed if play() isn’t called on the AudioStreamPlayer2D.
- This isn’t observed if there is no mp3 in AudioStreamPlayer2D.
- This is only observed with AudioStreamPlayer2D and not the parent: AudioStreamPlayer.
- The played sound appears to randomly be closer or further on scene reload.
Steps to reproduce
- Create the following scene:
- Add the following script to the root Node2D
extends Node2D
@onready var world : Node2D = $"."
@onready var viewport: SubViewport = $SubViewport
func _ready():
var world2d: World2D = world.get_world_2d()
viewport.world_2d = world2d
$AudioStreamPlayer2D.play()
func _process(_delta):
# Change this to anything. For me it's "1"
if Input.is_action_just_pressed("DebugKeyAction1"):
get_tree().reload_current_scene()
# Convenience.
if Input.is_action_just_pressed("f"):
if DisplayServer.window_get_mode() == DisplayServer.WINDOW_MODE_FULLSCREEN:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_MAXIMIZED)
else:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
- Run project
- Press DebugKeyAction1 (reload the scene). If it doesn't crash, keep trying. If it still doesn't crash, reload project and try again.
Minimal reproduction project (MRP)
There's been a huge rework by @KoBeWi in current dev builds (4.3) where all AudioStreamPlayers share the same underlying code, which was previously not the case. As a result, there's a possibility the bug may have been accidentally addressed. Could you try to reproduce this issue in the latest dev build?
Still crashes on master:
CrashHandlerException: Program crashed
Engine version: Godot Engine v4.3.dev.custom_build (9b94c80e9aff2a4f363ae6d8e2bbe837aa5876bc)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[0] Node::is_readable_from_caller_thread (C:\godot_source\scene\main\node.h:574)
[1] Node::is_readable_from_caller_thread (C:\godot_source\scene\main\node.h:574)
[2] Node2D::get_global_position (C:\godot_source\scene\2d\node_2d.cpp:290)
[3] AudioStreamPlayer2D::_update_panning (C:\godot_source\scene\2d\audio_stream_player_2d.cpp:145)
[4] AudioStreamPlayer2D::_notification (C:\godot_source\scene\2d\audio_stream_player_2d.cpp:63)
[5] AudioStreamPlayer2D::_notificationv (C:\godot_source\scene\2d\audio_stream_player_2d.h:42)
[6] Object::notification (C:\godot_source\core\object\object.cpp:849)
[7] SceneTree::_process_group (C:\godot_source\scene\main\scene_tree.cpp:947)
[8] SceneTree::_process (C:\godot_source\scene\main\scene_tree.cpp:1035)
[9] SceneTree::physics_process (C:\godot_source\scene\main\scene_tree.cpp:472)
[10] Main::iteration (C:\godot_source\main\main.cpp:3967)
[11] OS_Windows::run (C:\godot_source\platform\windows\os_windows.cpp:1476)
[12] widechar_main (C:\godot_source\platform\windows\godot_windows.cpp:182)
[13] _main (C:\godot_source\platform\windows\godot_windows.cpp:204)
[14] main (C:\godot_source\platform\windows\godot_windows.cpp:218)
[15] WinMain (C:\godot_source\platform\windows\godot_windows.cpp:232)
[16] __scrt_common_main_seh (D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
[17] <couldn't map PC to fn name>
-- END OF BACKTRACE --
I think it is the same bug I was about to report. This is the backtrace from Visual Studio in Windows:
And here is the place:
Is there any work around for this?
Having this issue as well in my project running on Godot 4.2.0, which was narrowed down to my minimap system (which uses a sub-viewport) after some analysis. As my UI is a separate scene from the main game scene, I attempted a workaround of only creating the sub viewport when the level is entered and freeing it when the level is exited, but even though the previous sub-viewports should be removed from the game and unable to cause any errors, I still experienced crashes.
Hopefully this information helps.
If anyone else encounters this issue, i think i've found a crude workaround that works
In my project i was removing a level scene that had a subviewport that shared the world2d with the main viewport, now when i switch levels i also replace the main viewport world2d with a new one (get_viewport().world_2d = World2D.new())
I don't know if this is a good approach or if it has other side effects i don't know, but it fixed the problem in my project at least
removing a level scene that had a subviewport that shared the world2d with the main viewport, now when i switch levels i also replace the main viewport world2d with a new one (
get_viewport().world_2d = World2D.new())
That is the first thing I did when I found the bug. It does not work (even I initially thought it did!). After sometime the bug materialized again. The only thing that I found it works was the patch that has not been approved yet:
https://github.com/godotengine/godot/pull/91123
I had to compile the engine by myself and apply the patch (3 lines of code which makes complete sense).
Strange, after doing the change in my project it stopped happening, and i even released the game (to a limited audience) and have yet to hear of any crashes
Guess i just got lucky :sweat_smile:
In my case it was not being caused by a reload but by a simple removal of the SubViewport using queue_free() on a parent node.
It's very easy to reproduce, but after I started removing the world_2d from the SubViewport before calling queue_free() it stopped crashing: sub_viewport.world_2d = World2D.new(). Edit: it would probably be better to do this on _exit_tree() or on NOTIFICATION_PREDELETE.
I imagine #91123 would also fix it as it removes the SubViewport from World2D in the same way as set_world_2d: https://github.com/godotengine/godot/blob/705b7a0b0bd535c95e4e8fb439f3d84b3fb4f427/scene/main/viewport.cpp#L1191
I'm looking for a workaround for this. I don't want to develop my game on a custom build or wait for 4.4.
I tried the suggestion of using sub_viewport.world_2d = World2D.new() . Tried it both on tree_exiting and on tree_exited, but as soon as that line is called, I get a different crash with a very long backtrace. It seem that method now cause an instant crash every time in Godot 4.3.
I tried replacing it with sub_viewport.world_2d = null
This seem to have fixed the crashes, but I now get a warning "Invalid world_2d" every time this is called, so I assume this is bad practice.
If anyone has a suggestion for another way to get around this, please reply. Thanks!
This will probably be in 4.3.1, so no need to wait for 4.4
@DinoMC I vaguely remember that after finding this thread while having a similar problem, the exact suggestions from this thread weren't working for me either. But this code worked fine on both 4.2 and on 4.3 right now:
extends Node2D
@onready var viewport:SubViewport = %SubViewport
func _ready():
viewport.world_2d = get_parent().get_viewport().world_2d
func _exit_tree():
viewport.world_2d = World2D.new()
I don't remember why exactly (probably should've added comments :D), but I think the problem was that get_viewport().world_2d (as suggested in previous comments) and get_parent().get_viewport().world_2d (my current version) was behaving differently.
@mynameiswhm In my case the _ready() func is working great, I'm actually using the following version :
viewport.world_2d = get_tree().root.get_viewport().world_2d
It's the _exit_tree() func that crash instantly, as soon as viewport.world_2d = World2D.new() is run. If I put a breakpoint there, I crash as soon as I click to step to the next line.
Whereas viewport.world_2d = null works (but send a warning)