Skip to content

Render Dependency Graph

浅说一下在 ChikaEngine 中实现的 RDG

早期版本可以看到,就是反向做了一个可达性剔除,同时保留所有的SideEffect节点(简单理解为很重要的,不能被剔除的节点)

重构之后,先说说RDG的Compile过程 ——

Compile

RDG 的编译过程,就是把 Render Pass 和资源的读写进行一个排序,同时计算其生命周期

可以查阅RenderGraphl.cpp在仓库中具体实现,下面简单说说 ——

Step 01. 清空

首先清空之前的所有状态,比如 Textures / Buffer / Pass/ 上次的编译结果 等

Step 02. 记录并且检查

先跑一边所有的 pass,记录下依赖关系,以在后面可以跑拓扑排序;同时在遍历的时候进行check,判断图是否合法

核心存储结构

C++
std::unordered_map<RGTextureHandle, RGPassHandle> lastTextureWriter;
std::unordered_map<RGBufferHandle, RGPassHandle> lastBufferWriter;
std::unordered_map<RGTextureHandle, std::vector<RGPassHandle>> textureReaders;
std::unordered_map<RGBufferHandle, std::vector<RGPassHandle>> bufferReaders;

lastTextureWriter 记录某个 Texture 最后是由哪个 Pass 写入的 lastBufferWriter 同理,但是记录的是 Buffer textureReaders 记录某个 Texture 在上一次写入之后,被哪些 Pass 读过 bufferReaders 同理,但是记录的是 Buffer

在遍历的过程中如果遇到非法直接提前返回

Step 03. Culling

这边就是最早版本的可达性检验的保留,

C++
for (const RGPassHandle handle : m_passInsertionOrder)
{
    const RGPass* pass = m_passes.Get(handle);
    if (pass && pass->hasSideEffects)
        markLive(handle);
}

此处 markLive 会递归保留依赖链,然后剔除没有连接到最终输出的 Pass

Step 04. 拓扑排序生成执行列表

(就是普通的拓扑排序)

在排序的过程中跳过无效的或者被剔除的 Pass,然后如果有环的话也报出来

Step 05. 生命周期计算

直接看 Texture 生命周期更新

每当当前 Pass 使用一个 Texture,就更新:

resource->firstUsePassIdx = std::min(resource->firstUsePassIdx, passIndex);resource->lastUsePassIdx = passIndex;++resource->refCount;

含义——

字段更新方式含义
firstUsePassIdx取最小值第一次使用
lastUsePassIdx直接覆盖最后一次使用
refCount自增使用次数

Buffer 同理

这样之后的 Debug 面板等就可以利用记录下来的信息做可视化等

Released under the MIT License.