(论文)[2019-SIG-ITalk] Global Adaptive Sampling Hierarchies in Production Ray Tracing

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 和树上所有有交集的节点都标记为已完成,则停止渲染,否则正常渲染

Adapting to New Information

  • 早期,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 变化更加敏感(也让树更平衡)
    • 边界最后使用 round 操作

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
  • 不是太懂这个