0%
GAS
- Global Adaptive Sampling Hierarchies in Production Ray Tracing
- SIGGRAPH ’19 Talks
- 作者:Keith Jeffery
- DreamWorks Animation
- MoonRay 渲染器(已开源)
Introduction
Framework
- a framework for predefined tiles
- MoonRay
- 每一个线程渲染 \(8\times8\)
的像素,减小线程冲突(thread contention)
- 对后续的 secondary rays 进行了 queue/sort 处理,但是对 primary rays
没有处理
- tilling 结构对于缓存一致性(primary cache coherency)比较友好
- 维护一棵二叉树,内部节点和叶子节点可以被标记为已完成
- 渲染任务开始的时候,如果当前 tile
和树上所有有交集的节点都标记为已完成,则停止渲染,否则正常渲染
- 早期,MoonRay 为了避免线程冲突,没有使用 HASC 中的树结构,而是每个
tile 只检查自己内部的 error
- 这样导致了一些
artifacts,某些区域过早停止,导致没有发现一些复杂现象(caustic 等)
- 即使是实现了 HASC 中的树结构,也还是会存在
artifacts(但是和上面的不太一样,具体咋样没说)
- conflicting desires
- early stopping
- the adaption to newly-discovered image features
- HASC 算法中,一个叶子节点一旦被终止,则内部像素不可能被重新更新
- 论文的算法中,每一个 pass 都重建树,一旦发现某一个节点中的 error
增大了,则这个节点会 re-split(通过树的重建),继续被更新
Floating-point image-space
division
- HASC 中的树的节点使用整数边界
- 直接使用整数边界,则划分位置是一个阶跃函数,需要 error
发生巨大的变化才会导致划分位置的变化
- 我们的划分方式,让划分位置对新发现的 error
变化更加敏感(也让树更平衡)
Sample consistency
- 有的像素可能不生成光线
- 用一个 buffer 去记录每一个像素需要分配的的 primary sample 数量
- 使用这个 buffer 里面的值去生成对应像素的样本,而不是使用一个整个
image 都一样的 spp 值
- 其他想法(和这个 Talk
无关)
- 在每个线程(tile)内部进行分配(\(8\times8\)),这样每个线程执行的光线数量是一样的
Threading
- 线程冲突是比较关键的问题
- 每一个 tile 是独立的,但是共用一棵树
- reading and updating the tree 需要特别小心
Barrier-free tree updates
- 每一个 pass 都需要更新 tree,这里需要使用一个 thread-barrier
去进行同步(等待 pass 完成)
- MoonRay 渲染器的默认行为
- 按照 Morton Curve 的顺序渲染 tiles(primary rays 的缓存一致性)
- 象限之间有先后顺序的(看下图)
- 因此可以维护 4 trees,每一个对应一个象限
- 树的更新原则:last one out turn off the lights
- 莫顿码示意图
- 在计算 error 的时候,象限树之间 overlap 一个 tile
的大小,来消除交界处的 artifacts
- 每一棵象限树维护一个原子计数器(atomic counter)
- 初始设置为 tiles 的个数
- 每一个 tiles 访问树的时候,将其计数 -1
- 当最后一个 tile 访问的时候,计数为 0,此时将树锁住,然后更新
- 锁和原子变量之间没有同步,加锁之后马上将原子变量增加,防止其他线程修改
- 整个流程:根据树给 tile 分配光线数量 \(\to\) tile 开始渲染工作 \(\to\) 更新树 \(\to\) 分配 \(\to\cdots\)
Reader-writer locks
- 读是写的 6500 多倍(电影相片)
- 因此加两个锁:shared reader locks,writer lock
Reducing mutex contention
- 实际上 shared reader locks 存在冲突(?)
- 使用一个 locks 数组,使用方式
- 位置是 \((x,y)\) 的获取 \(y(p/2+1)+x\)
- \(p\)
表示大于等于数组长度的二次幂(最小二次幂)
- 目的是:no neighbors in 2D space access the same mutex
- 不是太懂这个