vault backup: 2025-06-25 23:32:48
This commit is contained in:
parent
33539f99f0
commit
a92bd7ebd9
104
03-UnrealEngine/Rendering/RenderFeature/FRenderCommandFence.md
Normal file
104
03-UnrealEngine/Rendering/RenderFeature/FRenderCommandFence.md
Normal file
@ -0,0 +1,104 @@
|
||||
---
|
||||
title: FRenderCommandFence
|
||||
date: 2025-06-25 23:11:25
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||
在 Unreal Engine 5 (UE5) 中,`FRenderCommandFence` 是一个关键的**线程同步工具**,主要用于协调**游戏线程(Game Thread)** 和**渲染线程(Render Thread)** 之间的执行顺序。它的核心作用是确保渲染相关的操作(如资源创建、更新或销毁)在特定时间点前完成,避免多线程环境下的竞态条件。
|
||||
|
||||
---
|
||||
|
||||
### 核心作用
|
||||
1. **跨线程同步**
|
||||
- 游戏线程向渲染线程提交渲染命令后,这些命令不会立即执行(渲染线程有自己的任务队列)。
|
||||
- `FRenderCommandFence` 允许游戏线程**阻塞等待**,直到所有已提交的渲染命令(包括栅栏之前的所有命令)在渲染线程中执行完毕。
|
||||
2. **确保资源安全**
|
||||
- 当游戏线程需要操作渲染资源(如纹理、网格)时,必须确保渲染线程不再使用这些资源(例如释放 `UTexture`)。
|
||||
- 通过栅栏同步,可以安全地销毁或修改资源,避免访问无效内存。
|
||||
|
||||
---
|
||||
|
||||
### 关键方法
|
||||
- **`BeginFence()`**
|
||||
在渲染命令队列中插入一个“栅栏标记”,表示同步点。调用后游戏线程可以继续执行其他任务。
|
||||
- **`Wait(bool bProcessGameThreadTasks = false)`**
|
||||
- 阻塞游戏线程,直到渲染线程处理到栅栏位置(即执行完栅栏之前的所有渲染命令)。
|
||||
- `bProcessGameThreadTasks`: 若为 `true`,等待期间允许游戏线程处理其他任务(如消息泵)。
|
||||
- **`IsFenceComplete()`**
|
||||
非阻塞检查栅栏是否已完成(渲染线程是否已越过栅栏点)。
|
||||
|
||||
---
|
||||
|
||||
### 典型使用场景
|
||||
|
||||
#### 1. 安全释放渲染资源
|
||||
```c++
|
||||
// 游戏线程代码
|
||||
void ReleaseTexture(UTexture* Texture)
|
||||
{
|
||||
// 将资源释放命令提交到渲染线程
|
||||
ENQUEUE_RENDER_COMMAND(ReleaseTextureCommand)(
|
||||
[Texture](FRHICommandListImmediate& RHICmdList) {
|
||||
Texture->ReleaseResource();
|
||||
}
|
||||
);
|
||||
|
||||
// 创建栅栏并等待释放完成
|
||||
FRenderCommandFence Fence;
|
||||
Fence.BeginFence();
|
||||
Fence.Wait(); // 阻塞直到渲染线程执行完 ReleaseResource()
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 确保渲染操作完成后再继续
|
||||
```c++
|
||||
// 更新动态纹理后确保数据已提交到GPU
|
||||
void UpdateDynamicTexture()
|
||||
{
|
||||
// 提交更新命令到渲染线程
|
||||
ENQUEUE_RENDER_COMMAND(UpdateTexture)(
|
||||
[...](...) { /* 更新纹理数据 */ }
|
||||
);
|
||||
|
||||
FRenderCommandFence Fence;
|
||||
Fence.BeginFence();
|
||||
Fence.Wait(); // 等待纹理更新完成
|
||||
// 此时可以安全读取纹理或进行后续操作
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 异步加载资源时同步
|
||||
```c++
|
||||
void LoadAssetAsync()
|
||||
{
|
||||
StreamableManager.RequestAsyncLoad(..., [this]()
|
||||
{
|
||||
// 资源加载完成后,确保渲染线程初始化完毕
|
||||
FRenderCommandFence Fence;
|
||||
Fence.BeginFence();
|
||||
Fence.Wait(); // 等待渲染线程处理完资源初始化
|
||||
OnAssetLoaded();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 注意事项
|
||||
1. **性能影响**
|
||||
`Wait()` 会阻塞游戏线程,过度使用可能导致帧率下降。应仅在必要时同步(如资源卸载)。
|
||||
2. **替代方案**
|
||||
- 对于非紧急任务,可用 `AsyncTask` 或 `TaskGraph` 异步处理。
|
||||
- 使用 `FGraphEvent` 实现无阻塞等待。
|
||||
3. **与 `FlushRenderingCommands()` 的区别**
|
||||
`FlushRenderingCommands()` 会强制**立即刷新**整个渲染命令队列(更重操作),而 `FRenderCommandFence` 只等待到特定同步点,更轻量可控。
|
||||
|
||||
---
|
||||
|
||||
### 总结
|
||||
|
||||
`FRenderCommandFence` 是 UE5 多线程架构中的**安全阀**,通过它开发者可以:
|
||||
✅ 确保渲染操作在指定时间点前完成
|
||||
✅ 安全操作渲染资源(创建/更新/销毁)
|
||||
✅ 避免游戏线程与渲染线程的竞态条件
|
@ -57,10 +57,107 @@ rating: ⭐
|
||||
- TerrainAndSpawnablesManagement()
|
||||
|
||||
### ReadbacksManagement()
|
||||
相关线程安全变量:
|
||||
- **bProcessingHeightRetrieval**
|
||||
- **bProcessingHeightRetrievalRT**
|
||||
- ThreadSafe
|
||||
- bool
|
||||
- **bProcessingHeightRetrieval**
|
||||
- **bProcessingHeightRetrievalRT**
|
||||
- FSWShareableSamplePoints
|
||||
- PointsPendingReadBacks
|
||||
- FSWColorRead
|
||||
- ReadBackHeightData
|
||||
- FRenderCommandFence
|
||||
- HeightReadBackFence
|
||||
|
||||
```c++
|
||||
void AShaderWorldActor::ReadbacksManagement()
|
||||
{
|
||||
if (!bProcessingHeightRetrieval.IsValid())
|
||||
bProcessingHeightRetrieval = MakeShared<FThreadSafeBool, ESPMode::ThreadSafe>();
|
||||
if (!bProcessingHeightRetrievalRT.IsValid())
|
||||
bProcessingHeightRetrievalRT = MakeShared<FThreadSafeBool, ESPMode::ThreadSafe>();
|
||||
|
||||
//PointsPendingReadBacks存储从GPU回读的采样坐标位置;在RetrieveHeightAt()调用HeightReadBackFence.BeginFence(true)开启渲染栅栏,不开启这段逻辑不会执行。
|
||||
if ((*bProcessingHeightRetrieval.Get()) && (*bProcessingHeightRetrievalRT.Get()) && HeightReadBackFence.IsFenceComplete() && PointsPendingReadBacks.IsValid())
|
||||
{
|
||||
// ReadBackHeightData uin8 贴图数据
|
||||
if (!ReadBackHeightData.IsValid())
|
||||
return;
|
||||
|
||||
//For now we do Compute samples on a rendertarget 5x5, therefore 25 positions evaluated per request.
|
||||
//每次请求可以读取25个点。
|
||||
const int NumOfVertex = 25;
|
||||
|
||||
PositionsOfReadBacks.Empty();
|
||||
PositionsOfReadBacks.AddUninitialized(25);
|
||||
|
||||
uint8* ReadData8 = (uint8*)ReadBackHeightData->ReadData.GetData();
|
||||
uint16 MaterialIndice = 0;
|
||||
for (int32 k = 0; k < NumOfVertex; k++)
|
||||
{
|
||||
FVector3f& PositionSample = PositionsOfReadBacks[k];
|
||||
|
||||
if (RendererAPI == EGeoRenderingAPI::OpenGL)
|
||||
{
|
||||
int X = k % 5;
|
||||
int Y = k / 5;
|
||||
|
||||
int index = X + Y * 5;
|
||||
|
||||
Y = (5 - 1) - Y;
|
||||
|
||||
index = X + Y * 5;
|
||||
|
||||
PositionSample = FVector3f(PointsPendingReadBacks->PositionsXY[2 * k], PointsPendingReadBacks->PositionsXY[2 * k + 1], GetHeightFromGPURead(&ReadData8[index * 4], MaterialIndice) / HeightScale);
|
||||
}
|
||||
else
|
||||
PositionSample = FVector3f(PointsPendingReadBacks->PositionsXY[2 * k], PointsPendingReadBacks->PositionsXY[2 * k + 1], GetHeightFromGPURead(&ReadData8[k * 4], MaterialIndice) / HeightScale);
|
||||
|
||||
}
|
||||
|
||||
if(HeightRetrieveDelegate.ExecuteIfBound(PositionsOfReadBacks))
|
||||
{
|
||||
//SW_LOG("HeightRetrieveDelegate.ExecuteIfBound(PositionsOfReadBacks) PositionsOfReadBacks[0] %s",*PositionsOfReadBacks[0].ToString())
|
||||
}
|
||||
else
|
||||
{
|
||||
//SW_LOG("Fail HeightRetrieveDelegate.ExecuteIfBound(PositionsOfReadBacks)")
|
||||
}
|
||||
|
||||
bProcessingHeightRetrieval->AtomicSet(false);
|
||||
bProcessingHeightRetrievalRT->AtomicSet(false);
|
||||
}
|
||||
|
||||
//For now MinMax is here
|
||||
//处理FClipMapMeshElement的最大最小队列?
|
||||
if (UseSegmented())
|
||||
{
|
||||
if(WorldHasBounds_OnRebuild)
|
||||
{
|
||||
FClipMapMeshElement& MeshEl = FarLOD_BoundedWorld;
|
||||
|
||||
if (MeshEl.MinMaxQueue.Num() > 0)
|
||||
{
|
||||
MeshEl.ProcessMinMaxQueue(SWorldSubsystem);
|
||||
}
|
||||
}
|
||||
|
||||
for (FClipMapMeshElement& MeshEl : Meshes)
|
||||
{
|
||||
|
||||
if (MeshEl.MinMaxQueue.Num() > 0)
|
||||
{
|
||||
MeshEl.ProcessMinMaxQueue(SWorldSubsystem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### CollisionManagement()
|
||||
|
||||
### SpawnablesManagement()
|
||||
|
||||
### TerrainAndSpawnablesManagement()
|
||||
|
||||
## PreEditChange() / PostEditChangeProperty()
|
||||
**PreEditChange()** 主要针对以下两个变量的设置:
|
||||
|
Loading…
x
Reference in New Issue
Block a user