Buggy Behavior when using TileSetAtlasSource with a Thread
Tested versions
Reproducible in 4.4.1stable, 4.5beta1
System information
Godot v4.4.1.stable - Windows 11 (build 22631) - Multi-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3060 (NVIDIA; 32.0.15.6094) - AMD Ryzen 7 5800X 8-Core Processor (16 threads)
Issue description
Using create_tile on TileSetAtlasSource can cause unpredictable errors and crashes. These do not occur on the main thread.
Steps to reproduce
This code attached to a node will trigger crashes, and occasionally these errors:
<C++ Source> servers/rendering/renderer_rd/storage_rd/texture_storage.cpp:615 @ canvas_texture_set_channel()
<C++ Error> Method/function failed. Returning: false
<C++ Source> core/object/object.cpp:1490 @ is_connected()
<C++ Error> Condition "!p_callable.is_valid()" is true. Returning: ERR_INVALID_PARAMETER
<C++ Source> core/object/object.cpp:1418 @ connect()
Although icon.svg is used as an example, using larger textures such as 512x512 to fill in tiles this way can cause these types of errors (where x and y are not consistent in value or frequency):
E 0:00:00:703 get_tile_texture_region: TileSetAtlasSource has no tile at (x, y). <C++ Error> Condition "!tiles.has(p_atlas_coords)" is true. Returning: Rect2i() <C++ Source> scene/resources/2d/tile_set.cpp:5303 @ get_tile_texture_region()
extends Node2D
var thread: Thread = Thread.new()
func _ready() -> void:
thread.start(create_tiles)
func create_tiles():
var new_tileset_atlas_source: TileSetAtlasSource
for i in range(64):
new_tileset_atlas_source = TileSetAtlasSource.new()
new_tileset_atlas_source.texture = preload("res://icon.svg")
for x in 8:
for y in 8:
new_tileset_atlas_source.create_tile(Vector2i(x, y))
Minimal reproduction project (MRP)
Please provide an MRP to make this easier to test and fix:
- A small Godot project which reproduces the issue, with no unnecessary files included. Be sure to not include the
.godotfolder in the archive (but keepproject.godot). - Having an MRP is very important for contributors to be able to reproduce the bug in the same way that you are experiencing it. When testing a potential fix for the issue, contributors will use the MRP to validate that the fix is working as intended.
- Drag and drop a ZIP archive to upload it (max 10 MB). Do not select another field until the project is done uploading.
- Note for C# users: If your issue is not C#-specific, please upload a minimal reproduction project written in GDScript. This will make it easier for contributors to reproduce the issue locally as not everyone has a .NET setup available.
MRP provided.
I believe I am also encountering this issue, but in my case I am using ResourceLoader.LoadThreadedRequest (and waiting for the status to be loaded) to load scenes contained tilemaps built in the editor. It is intermittitent in my case but when it does I see the following error:
E 0:00:04:790 get_tile_texture_region: TileSetAtlasSource has no tile at (1, 1). <C++ Error> Condition "!tiles.has(p_atlas_coords)" is true. Returning: Rect2i() <C++ Source> scene/resources/2d/tile_set.cpp:5303 @ get_tile_texture_region()
E 0:00:04:791 canvas_texture_set_channel: Parameter "ct" is null. <C++ Source> servers/rendering/renderer_rd/storage_rd/texture_storage.cpp:615 @ canvas_texture_set_channel()
This is in 4.4.1.stable.mono
Can somewhat reproduce (not sure about the exact errors) on:
Godot v4.6.dev (25203e24c) - Windows 11 (build 26100) - Multi-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4080 SUPER (NVIDIA; 32.0.15.8180) - AMD Ryzen 7 7800X3D 8-Core Processor (16 threads) - 63.11 GiB memory
The MRP always crashes, when running the project, with many different Debugger logs and backtraces, here is what I got (the backtrace is only the unique part to make it more readable, the common part of the backtrace is at the end of the comment):
1
Errors
None
Backtrace
[1] Ref<Texture2D>::operator==(Ref<Texture2D> const&) const (./core/object/ref_counted.h:109)
[2] CanvasTexture::set_diffuse_texture(Ref<Texture2D> const&) (scene/main/canvas_item.cpp:1788)
[3] TileSetAtlasSource::_update_padded_texture() (scene/resources/2d/tile_set.cpp:5725)
2
Errors
Backtrace
[1] unsigned int HashMapHasherDefault::hash<int>(int const&) (./core/templates/hashfuncs.h:179)
[2] HashMap<int, TileData*, HashMapHasherDefault, HashMapComparatorDefault<int, void>, DefaultTypedAllocator<HashMapElement<int, TileData*> > >::_hash(int const&) (./core/templates/hash_map.h:86)
[3] HashMap<int, TileData*, HashMapHasherDefault, HashMapComparatorDefault<int, void>, DefaultTypedAllocator<HashMapElement<int, TileData*> > >::insert(int const&, TileData* const&, bool) (./core/templates/hash_map.h:572)
[4] HashMap<int, TileData*, HashMapHasherDefault, HashMapComparatorDefault<int, void>, DefaultTypedAllocator<HashMapElement<int, TileData*> > >::HashMap(HashMap<int, TileData*, HashMapHasherDefault, HashMapComparatorDefault<int, void>, DefaultTypedAllocator<HashMapElement<int, TileData*> > > const&) (./core/templates/hash_map.h:593)
[5] TileSetAtlasSource::TileAlternativesData::TileAlternativesData(TileSetAtlasSource::TileAlternativesData const&) (scene/resources/2d/tile_set.h:637)
[6] KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData>::KeyValue(KeyValue<Vector2i, TileSetAtlasSource::TileAlternativesData> const&) (./core/templates/pair.h:71)
[7] TileSetAtlasSource::_create_padded_image_texture(Ref<Texture2D> const&) (scene/resources/2d/tile_set.cpp:5640)
[8] TileSetAtlasSource::_update_padded_texture() (scene/resources/2d/tile_set.cpp:5724)
3
Errors
Backtrace
Same as 2
4
Errors
Backtrace
Only happened once and I didn't copy the backtrace
5
Errors
Backtrace
[1] HashMap<Vector2i, TileSetAtlasSource::TileAlternativesData, HashMapHasherDefault, HashMapComparatorDefault<Vector2i, void>, DefaultTypedAllocator<HashMapElement<Vector2i, TileSetAtlasSource::TileAlternativesData> > >::operator[](Vector2i const&) const (./core/templates/hash_map.h:554)
[2] TileSetAtlasSource::get_tile_texture_region(Vector2i, int) const (scene/resources/2d/tile_set.cpp:5285)
[3] TileSetAtlasSource::_create_padded_image_texture(Ref<Texture2D> const&) (scene/resources/2d/tile_set.cpp:5643)
[4] TileSetAtlasSource::_update_padded_texture() (scene/resources/2d/tile_set.cpp:5724)
6 (with varying amount had up to 352 errors)
Errors
Backtrace
[1] Vector2i::operator==(Vector2i const&) const (./core/math/vector2i.h:232)
[2] HashMapComparatorDefault<Vector2i, void>::compare(Vector2i const&, Vector2i const&) (./core/templates/hashfuncs.h:303)
[3] HashMap<Vector2i, TileSetAtlasSource::TileAlternativesData, HashMapHasherDefault, HashMapComparatorDefault<Vector2i, void>, DefaultTypedAllocator<HashMapElement<Vector2i, TileSetAtlasSource::TileAlternativesData> > >::_lookup_idx_unchecked(Vector2i const&, unsigned int, unsigned int&) const (./core/templates/hash_map.h:130)
[4] HashMap<Vector2i, TileSetAtlasSource::TileAlternativesData, HashMapHasherDefault, HashMapComparatorDefault<Vector2i, void>, DefaultTypedAllocator<HashMapElement<Vector2i, TileSetAtlasSource::TileAlternativesData> > >::_lookup_idx(Vector2i const&, unsigned int&) const (./core/templates/hash_map.h:111)
[5] HashMap<Vector2i, TileSetAtlasSource::TileAlternativesData, HashMapHasherDefault, HashMapComparatorDefault<Vector2i, void>, DefaultTypedAllocator<HashMapElement<Vector2i, TileSetAtlasSource::TileAlternativesData> > >::has(Vector2i const&) const (./core/templates/hash_map.h:322)
[6] TileSetAtlasSource::get_tile_texture_region(Vector2i, int) const (scene/resources/2d/tile_set.cpp:5284)
[7] TileSetAtlasSource::_create_padded_image_texture(Ref<Texture2D> const&) (scene/resources/2d/tile_set.cpp:5643)
[8] TileSetAtlasSource::_update_padded_texture() (scene/resources/2d/tile_set.cpp:5724)
Common backtrace part:
================================================================
CrashHandlerException: Program crashed with signal 11
Engine version: Godot Engine v4.6.dev.custom_build (25203e24c403afdba4370249204b83cb830e4809)
Dumping the backtrace. Please include this when reporting the bug on: https://github.com/godotengine/godot/issues
[4] void call_with_variant_args_helper<TileSetAtlasSource>(TileSetAtlasSource*, void (TileSetAtlasSource::*)(), Variant const**, Callable::CallError&, IndexSequence<>) (./core/variant/binder_common.h:224)
[5] void call_with_variant_args<TileSetAtlasSource>(TileSetAtlasSource*, void (TileSetAtlasSource::*)(), Variant const**, int, Callable::CallError&) (./core/variant/binder_common.h:338)
[6] CallableCustomMethodPointer<TileSetAtlasSource, void>::call(Variant const**, int, Variant&, Callable::CallError&) const (./core/object/callable_method_pointer.h:103)
[7] Callable::callp(Variant const**, int, Variant&, Callable::CallError&) const (core/variant/callable.cpp:57)
[8] CallQueue::_call_function(Callable const&, Variant const*, int, bool) (core/object/message_queue.cpp:220)
[9] CallQueue::flush() (core/object/message_queue.cpp:268)
[10] SceneTree::physics_process(double) (scene/main/scene_tree.cpp:647)
[11] Main::iteration() (main/main.cpp:4831)
[12] OS_Windows::run() (platform/windows/os_windows.cpp:2346)
[13] widechar_main(int, wchar_t**) (platform/windows/godot_windows.cpp:99)
[14] _main() (platform/windows/godot_windows.cpp:126)
[15] main (platform/windows/godot_windows.cpp:145)
[16] ShimMainCRTStartup (platform/windows/cpu_feature_validation.c:74)
-- END OF C++ BACKTRACE --
================================================================