godotengine / godotengine/godot

AnimationTree.advance() on custom thread breaks update of the skeleton.

Open
#101,573 3 comments 0 reactions 0 assignees View on GitHub
discussion needs testing topic:animation
Dominant language
C++
Stars
117k
Forks
26.8k
PR merge metrics
PR metrics pending

Description

### Tested versions

v4.3.stable.official [77dcf97d8]

### System information

Godot v4.3.stable - Windows 10.0.19045 - Vulkan (Forward+) - dedicated GeForce 920M - Intel(R) Core(TM) i7-5500U CPU @ 2.40GHz (4 Threads)

### Issue description

I've been unhappy with performance of skeletal animations so I moved everything to animation calculation on a **custom** thread inherited from Godot `Thread`. And is turns out that its not supported, and you'll say "of cause its not". But! You can make it work. You need some tricks. You need to call `advance()` on main thread once to create cache and then it can be updated. Also it need unique instances of `AnimationTreeNode`, sharing animation root is not an option here.

At first it gave me **4x** time performance boost as well if was not interfering with main thread ( _as oppose to running animation on **SubThread** which slows down main thread. And on **SubThread** it works fine, nothing breaks._ )

So while its running at first ok, after some time **skeleton** that is connected to `AnimationTree` stops updating positions. But transforms are still being calculated, animation still going. As you can see video below, I'm coping (_using simple_ `SkeletonModifier3D`) transforms from `AnimationTree` skeleton to **another skeleton** and its still being animated.

https://github.com/user-attachments/assets/7cc009f2-8165-4f69-b0e2-5bb213618e95

Anyway I've decided to report it as a bug. Since its possible and I'm not only one who will try it. And I feel like its totally possible.

One thing is calling `advance()` on thread will print an error to the console.
That because of `emit_signal(SNAME("mixer_applied"));` in the `AnimationMixer::_process_animation`. I have my edited version of engine where I just did this:
```
if(Thread::is_main_thread())
emit_signal(SNAME("mixer_applied"));
```
Behavior of bug is the same, regardless.

### Steps to reproduce

Run test project provided and wait for a while. Sometimes you need to wait a minute. Sometimes its 30 minutes.

### Minimal reproduction project (MRP)

[test_manual_advance.zip](https://github.com/user-attachments/files/18421243/test_manual_advance.zip)

Contributor guide

Open the contributing guide

Research direction

Start by running the linked minimal reproduction project and waiting for the connected skeleton to stop updating. Then inspect AnimationTree.advance() and AnimationMixer::_process_animation, including the mixer_applied signal behavior described in the report. Done means identifying and fixing the custom-thread update failure without breaking the existing SubThread behavior, with a regression test if the relevant test location is established.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
game-dev
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.