- Cache Rules
- Cache timing
- Cache data holder
- FPrimitiveSceneInfo
- Cache invalidation
- Rule
- Invalidation Process
- A minimap of everything
- Configure cache strategy
- Mesh Processor
- Mesh Render Proxy
- Mesh Draw Command
Cache Rules
Now let’s talk about a new topic: the mesh command cache. Think about this question first:
If our mesh is a static cube and we are not changing its material or transform, then there is no need to rebuild the mesh batch or the mesh draw commands.
I recommend reading the official document about the mesh draw command caching system at this link. However, if you are busy, here is a helpful image from that page:
- The VF means vertex factory. We talked in
- The orange arrow indicates that updates occur every frame, while the blue arrow means that the results are cached.
- Because our cube is a simple static mesh component, it follows the lowest path, resulting in one mesh batch and the caching of all mesh draw commands until you modify it and invalidate the cache.
Cache timing
The main cache timing is UpdateAllPrimitiveSceneInfos
of FScene
. If a static mesh is added the first time, or if the cache is invalidated, this function will called to rebuild the cache.
Cache data holder
In a previous discussion about RenderProxy
→ FMeshBatch
, we use this article From Render Proxy to Mesh Batches to explain the process. The final step we mentioned is:
- It will call
FStaticMeshSceneProxy
::DrawStaticElements
. - This function will call
FStaticMeshSceneProxy
::GetMeshElement
, and return aFMeshBatch
.
We stopped at this chapter earlier, but let's dive in a little deeper now.
The resulting mesh batch is stored in a FPrimitiveSceneInfo
object which is used to cache data. This object includes:
TArray<class FStaticMeshBatch> StaticMeshes
: for caching static mesh batchesTArray<class FCachedMeshDrawCommandInfo> StaticMeshCommandInfos
: for caching static mesh command id
The real mesh draw command cache is inside theFScene
, calledCachedDrawLists
andCachedMeshDrawCommandStateBuckets
This is the first time we're encountering FPrimitiveSceneInfo
, so let's discuss it a bit more.
FPrimitiveSceneInfo
The official comment about this structure is
The renderer's internal state for a single UPrimitiveComponent. This has a one to one mapping with FPrimitiveSceneProxy, which is in the engine module.
This is a simple image to show the data relationships:
FPrimitiveSceneInfo
directly caches the static mesh batches. But indirectly caches the mesh draw command with a struct calledFStaticMeshCommandInfo
which holds an index to the real cacheCachedDrawLists
in theFScene
FCachedPassMeshDrawList CachedDrawLists[EMeshPass::Num]
can be treat as a dictionary with a key ofEMeshPass
. The element is aTSparseArray<FMeshDrawCommand> MeshDrawCommands
Cache invalidation
Rule
Any data that a Mesh Pass Processor reads in AddMeshBatch
is a dependency of the cached mesh draw commands. When that dependency changes, the cached commands must be invalidated.
A single primitive's cached commands can be invalidated withFPrimitiveSceneInfo::BeginDeferredUpdateStaticMeshes
. The entire scene's cached commands can be invalidated by settingScene->bScenesPrimitivesNeedStaticMeshElementUpdate
totrue
. This is a heavyweight operation and should be avoided during gameplay as it will cause a hitch in larger scenes.
Invalidation Process
As an example, you drag the cube a little
This is what happens:
- Handle your mouse move event ( drag event), or the property changes, or you set rendering related flags, or
MarkRenderStateDirty()
- End of frame
- UWorld::SendAllEndOfFrameUpdates
- UActorComponent::DoDeferredRenderUpdates_Concurrent
- Because the
bRenderStateDirty
is true, this component tries to recreate the render state withRecreateRenderState_Concurrent
- In
UPrimitiveComponent::CreateRenderState_Concurrent
, it callsGetWorld()->Scene->AddPrimitive
to add itself to theFScene
- Enqueue the task of add
PrimitiveSceneInfo
to the render thread - The render thread picks the task:
- Add the
PrimitiveSceneInfo
passed from the game thread toAddedPrimitiveSceneInfos
- Render thread update:
- FScene::UpdateAllPrimitiveSceneInfos
- Copy
AddedPrimitiveSceneInfos
toAddedLocalPrimitiveSceneInfos
- Copy
AddedLocalPrimitiveSceneInfos
toSceneInfosWithStaticDrawListUpdate
- For each
SceneInfosWithStaticDrawListUpdate
: - Call
FPrimitiveSceneInfo::CacheMeshDrawCommands
A minimap of everything
- We show two ticks in this image
- You can see the timing of
FRenderProxy
→FMeshBatch
→FMeshDrawCommands
and get cached. - In my environment, the real mesh command caching timing is deferred.
Configure cache strategy
Mesh Processor
In , we gave an example:
REGISTER_MESHPASSPROCESSOR_AND_PSOCOLLECTOR(DepthPass, CreateDepthPassProcessor, EShadingPath::Deferred, EMeshPass::DepthPass, EMeshPassFlags::CachedMeshCommands | EMeshPassFlags::MainView);
The final parameter EMeshPassFlags::CachedMeshCommands
is the key to report this FMeshCommandProcessor
should support caching.
Mesh Render Proxy
In GetViewRelevance
, you can configure if this render proxy is bStaticRelevance
or bDynamicRelevance
An example is FPrimitiveViewRelevance FStaticMeshSceneProxy::GetViewRelevance(const FSceneView* View) const
. Please check the source.
Mesh Draw Command
Check SupportsCachingMeshDrawCommands
in the PrimitiveSceneProxy.cpp
- MeshBatch only has one element:
MeshBatch.Elements.Num() == 1
- VertexFactory supports caching mesh draw commands:
MeshBatch.VertexFactory->GetType()->SupportsCachingMeshDrawCommands()
- Material doesn’t have external texture parameters.