Every frame the render loop spawned and destroyed OS threads via
std::async: the M2 doodad animation update, three secondary command
buffer recording tasks, up to N bone-matrix chunks in the M2 and
character renderers, and two collision floor queries while moving —
hundreds of thread create/destroy cycles per second. Route all of them
through a shared persistent core::ThreadPool sized to the machine.
Bone-matrix chunk dispatch now runs its last chunk on the calling
thread so the nested case (M2 update running on a pool worker while
submitting sub-work) always makes progress and cannot deadlock.