# Actor Pooling

## **Intro** <a href="#intro" id="intro"></a>

Actor pooling is a concept which allows us to swap in and out actors at runtime without creating and destroying them, by caching actors in a list called a **pool**. When a new actor of a particular type is required, if one exists in the pool we can grab that actor and use it instead of creating a new one, eliminating much of the overhead involved in creating an actor at the expense of using up some memory.

MSquared's actor pooling system is almost entirely standard, and this article will concentrate less on technical details and more on common usages together with the kinds of situations which can catch people out!

## Pooled Render Target Actors

The main use of the actor pooling system is in our LOD system for characters. With up to 20k players in a level where only the nearest \~35 are displayed in full fidelity (the rest being represented with [Crowd Rendering](/creation/unreal-development/features-and-tutorials/the-animated-crowd.md)), we need a way of efficiently assigning and removing render target actors from player MAs as they run around.

Actor pooling fulfills that brief by returning a render target to the pool when a player moves into the crowd, and grabbing a render target from the pool when they move out of the crowd.

{% hint style="info" %}
NOTE: This only applies to other clients' characters. The authoritative client's render target actor is not pooled, and cannot go into the crowd.
{% endhint %}

<figure><img src="/files/PcamVJYBzdpWCQnH5HMY" alt=""><figcaption><p>This example has 3 clients, with the max number in LOD0 set to 1. Of the other clients, one character will be a render target actor, and the other will be in the crowd. As you move around, and the other characters move in and out of the crowd, they will use the same <code>BP_Origin_PlayerCharacter</code>. Therefore, there will only ever be 2 actors, your one, and whichever other client is in LOD0.</p></figcaption></figure>

{% hint style="warning" %}
**When Actor Pooling Goes Wrong**

The main downside of actor pooling is that if e.g. a render target actor is moved to the pool, it retains any state on it that occurred during natural gameplay involving its previous owner. This can lead to **visual and logical inconsistencies**, where the newly-obtained render target is still doing things based on its state before it was returned to the pool.

Therefore, most of the work in actor pooling is in making sure that any such state is reset before the actor is returned to the pool (or after it’s taken from the pool, depending on what’s appropriate).

Typically if a visual or logical bug occurs only when swapping roles or when large amounts of people are running around, it’s likely to do with state not being reset during actor pooling.

The main implication of the above is that **any component on a player render target** should be checked for state which may cause issues during pooling when work is done on it.

We also have to be careful to gracefully handle any components which are **dynamically added or removed** as when pooling the actor they may need to be removed or added respectively.
{% endhint %}

## **Technical Usage** <a href="#technical-usage" id="technical-usage"></a>

**The lifecycle of pooled actors, and the relevant transition events is as follows:**

<figure><img src="/files/n6Rt2oSGWLcRygoBwkJj" alt=""><figcaption></figcaption></figure>

* To enable actor pooling on an actor or component, implement the `IMorpheusPooledActor` interface on it.
  * For the interface on the component to work, it will need to be on an actor that also implements the interface.
* To enable actor pooling on a render target, implement the `IMorpheusPooledRenderTarget` interface on it.
  * This is a more specialised version of `IMorpheusPooledActor`, that adds the `OnEndPlayToPoolWithOwner` event.

And that’s it! Everything else is handled for you. The interfaces provide three functions, and a fourth for the render target one specifically:

* `OnBeginPlayFromPool()` can be implemented to perform logic when an actor is taken from the pool. Logic such as making the actor visible again, or setting up the state based on the latest MorpheusActor associated with the actor is done here.
* `OnEndPlayToPool()` can be implemented to perform logic when an actor is returned to the pool. Logic such as hiding the actor, or cleaning up any added visuals is done here.
* `IsPoolingEnabled()` can be implemented to provide an additional conditional check for pooling. If false, the actor won't be pooled.\
  \&#xNAN;*NOTE: This interface function has no effect when implemented on a component; it will perform its begin/end logic regardless, dependent entirely on whether `IsPoolingEnabled()` returns `true` for its owning actor.*
* `OnEndPlayToPoolWithOwner()` on the **render target version only** is a version of `OnEndPlayToPool()` which provides a `MorpheusActor` owner as an argument. (This is because the association between the render target actor and its old MorpheusActor is cleaned up before it is returned to the pool)

<figure><img src="/files/Hah1k5RFpbPW1MjWbvte" alt=""><figcaption></figcaption></figure>

## **How to Test** <a href="#how-to-test" id="how-to-test"></a>

Naturally, each usage of actor pooling will require a different testing procedure due to it being specific to particular actors. However, a general solution for testing render targets or components on those render targets is to simulate targets moving into and out of LoD0.

We can do this by overriding the `PlayerClient.Rendering.NumInLoD0` live config value in the editor (or in live config, if testing a deployment) to e.g. 1 and then having 3 clients present. This means that one client can observe as the other two run in and out of LoD0.

Characters changing LOD levels can be easily triggered via the Morpheus Inspector - see [Morpheus Inspector](/creation/unreal-development/features-and-tutorials/inspector.md#rendering-related-options)

{% hint style="warning" %}
The following sections refer to functionality added in release v29
{% endhint %}

### "Lingering" delegates

If a render target actor has been returned to the pool, but hasn't unbound any delegates that it bound to on its previous Morpheus Actor (or any components on that Morpheus Actor), you could see strange bugs present themselves, since the pooled actor would still be listening to that old Morpheus Actor's events. If it then re-enters play out of the pool, being assigned a new Morpheus Actor, it could appear to be mostly working, but be listening to events on the old owner, instead of (or as well as) the new one.

We have added some additional warnings that will be printed if this is the case, to help track issues like this down.

We also have automatic logic, gated behind the `Pooling.RemoveDelegatesBoundToOldMorpheusActor` live config flag, to unbind such delegates. This should at least minimise the damage of not fully implementing actor pooling (the delegates will still need to be bound when the actor is returned to the pool though!)

<figure><img src="/files/jkHj83vQyzf84LgDnjd6" alt=""><figcaption><p>An example warning if there is a delegate that hasn't been cleaned up. In this example, we automatically unbind it.</p></figcaption></figure>

### Pooling Verbose Logging

If you have verbose logging enabled for `LogMorpheusActorPooling` category, it will print additional details on the actor returning to the pool, comparing it to the base class's default values.

These logs will print any differences from a reference actor, which hasn't yet begun play. Some of these may not be issues, but worth considering any differences that you don't expect, relevant to components/properties you added!

This can be set by doing e.g.:

* Using the `Log LogMorpheusActorPooling Verbose` command
* Adding the `LogMorpheusActorPooling=Verbose` line to the `[Core.Log]` section of your Engine `.ini` file.

<figure><img src="/files/Zw6tprfEE3NncktQjeLl" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.msquared.io/creation/unreal-development/features-and-tutorials/actor-pooling.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
