diff --git a/03-UnrealEngine/Rendering/RenderFeature/FRenderCommandFence.md b/03-UnrealEngine/Rendering/RenderFeature/FRenderCommandFence.md new file mode 100644 index 0000000..4b3578f --- /dev/null +++ b/03-UnrealEngine/Rendering/RenderFeature/FRenderCommandFence.md @@ -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 多线程架构中的**安全阀**,通过它开发者可以: +✅ 确保渲染操作在指定时间点前完成 +✅ 安全操作渲染资源(创建/更新/销毁) +✅ 避免游戏线程与渲染线程的竞态条件 \ No newline at end of file diff --git a/03-UnrealEngine/Rendering/RenderFeature/ShaderWorldPlugin/ShaderWorld.md b/03-UnrealEngine/Rendering/RenderFeature/ShaderWorldPlugin/ShaderWorld.md index d19ca6a..78eecdc 100644 --- a/03-UnrealEngine/Rendering/RenderFeature/ShaderWorldPlugin/ShaderWorld.md +++ b/03-UnrealEngine/Rendering/RenderFeature/ShaderWorldPlugin/ShaderWorld.md @@ -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(); + if (!bProcessingHeightRetrievalRT.IsValid()) + bProcessingHeightRetrievalRT = MakeShared(); + + //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()** 主要针对以下两个变量的设置: