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

Calling play() on AudioStreamPlayer2D causes crash without error log when World2D is shared with SubViewport and scene is reloaded. #89212

Closed
Tracked by #76797
HelpFunction opened this issue Mar 6, 2024 · 12 comments · Fixed by #91123

Comments

@HelpFunction
Copy link

Tested versions

v4.2.1.stable.official [b09f793]

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

  1. Create the following scene:
    image
  2. 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)
  1. Run project
  2. 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)

Audio2DViewport Crash.zip

@Mickeon
Copy link
Contributor

Mickeon commented Mar 6, 2024

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?

@KoBeWi
Copy link
Member

KoBeWi commented Mar 6, 2024

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 --

@ditiem-games
Copy link

I think it is the same bug I was about to report. This is the backtrace from Visual Studio in Windows:

image

And here is the place:

image

Is there any work around for this?

@Crusher48
Copy link

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.

aaronp64 added a commit to aaronp64/godot that referenced this issue Apr 24, 2024
Updated Viewport destructor to remove itself from World2D, to avoid World2D keeping invalid pointers.

Fixes godotengine#89212
@Megabjarne
Copy link

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

@ditiem-games
Copy link

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:

#91123

I had to compile the engine by myself and apply the patch (3 lines of code which makes complete sense).

@Megabjarne
Copy link

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 😅

@pheki
Copy link

pheki commented Jun 2, 2024

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:

if (world_2d.is_valid()) {

@DinoMC
Copy link

DinoMC commented Sep 4, 2024

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!

@AThousandShips
Copy link
Member

This will probably be in 4.3.1, so no need to wait for 4.4

@mynameiswhm
Copy link

@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.

@DinoMC
Copy link

DinoMC commented Sep 5, 2024

@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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.