Embree:流模式 - 聚集和分散如何工作以及什么是 pid 和 tid?
Embree: stream mode - how does gather and scatter work and what are pid and tid?
我正在尝试将我的应用程序从单射线相交升级到流相交。
我不太明白教程中显示的 gather
和 scatter
functions 怎么可能起作用
该示例定义了自定义扩展光线结构 Ray2
struct Ray2
{
Ray ray;
// ray extensions
float transparency; //!< accumulated transparency value
// we remember up to 16 hits to ignore duplicate hits
unsigned int firstHit, lastHit;
unsigned int hit_geomIDs[HIT_LIST_LENGTH];
unsigned int hit_primIDs[HIT_LIST_LENGTH];
};
然后它定义了这些Ray2
结构的数组:
Ray2 primary_stream[TILE_SIZE_X*TILE_SIZE_Y];
这个数组在调用交集方法之前设置为userRayExt:
primary_context.userRayExt = &primary_stream;
rtcIntersect1M(data.g_scene,&primary_context.context,(RTCRayHit*)&primary_stream,N,sizeof(Ray2));
现在,对于 embree 与几何相交的每个光线束,都会调用过滤器回调:
/* intersection filter function for streams of general packets */
void intersectionFilterN(const RTCFilterFunctionNArguments* args)
{
int* valid = args->valid;
const IntersectContext* context = (const IntersectContext*) args->context;
struct RTCRayHitN* rayN = (struct RTCRayHitN*)args->ray;
//struct RTCHitN* hitN = args->hit;
const unsigned int N = args->N;
/* avoid crashing when debug visualizations are used */
if (context == nullptr) return;
/* iterate over all rays in ray packet */
for (unsigned int ui=0; ui<N; ui+=1)
{
/* calculate loop and execution mask */
unsigned int vi = ui+0;
if (vi>=N) continue;
/* ignore inactive rays */
if (valid[vi] != -1) continue;
/* read ray/hit from ray structure */
RTCRayHit rtc_ray = rtcGetRayHitFromRayHitN(rayN,N,ui);
Ray* ray = (Ray*)&rtc_ray;
/* calculate transparency */
Vec3fa h = ray->org + ray->dir * ray->tfar;
float T = transparencyFunction(h);
/* ignore hit if completely transparent */
if (T >= 1.0f)
valid[vi] = 0;
/* otherwise accept hit and remember transparency */
else
{
/* decode ray IDs */
const unsigned int pid = ray->id / 1;
const unsigned int rid = ray->id % 1;
Ray2* ray2 = (Ray2*) context->userRayExt;
assert(ray2);
scatter(ray2->transparency,sizeof(Ray2),pid,rid,T);
}
}
}
这个方法的最后一行我没看懂
scatter(ray2->transparency,sizeof(Ray2),pid,rid,T);
我明白它应该做什么。它应该用 T 更新对应于跟踪光线的 Ray2
的透明度 属性。但是我没有得到 why/how 这有效,因为 scatter
的实现看起来像这样:
inline void scatter(float& ptr, const unsigned int stride, const unsigned int pid, const unsigned int rid, float v) {
((float*)(((char*)&ptr) + pid*stride))[rid] = v;
}
我会重新表述这个函数以便更好地提出我的问题(但如果我没记错的话它应该是完全等价的):
inline void scatter(float& ptr, const unsigned int stride, const unsigned int pid, const unsigned int rid, float v) {
float* uptr = ((float*)(((char*)&ptr) + pid*stride));
uptr[rid] = v;
}
所以,第一行对我来说仍然有意义。构造一个指向第一个 Ray2 结构的透明字段的指针,然后递增 tid * sizeof(Ray2)
- 这是有道理的,因为它将落在另一个 transparency
字段上,因为它递增 [=28] 的倍数=]
但下一行
uptr[rid] = v;
我一点都不明白。 uptr
是一个浮点指针,指向一个透明字段。因此,除非 rid
本身是 sizeof(Ray2)
的倍数,否则它根本不会指向其中一条光线的透明场。
pid
和 rid
计算为
const unsigned int pid = ray->id / 1;
const unsigned int rid = ray->id % 1;
我觉得很奇怪。这不总是和
一样吗
const unsigned int pid = ray->id;
const unsigned int rid = 0;
?
什么是 pid
和 rid
,为什么要这样计算?
我自己没有写过这个例子,所以很难猜出它的初衷是什么,但我认为线索就在你的观察中,对于 rid 和 pid 的计算, division/modulo by '1' 没有意义。
所以,if rid 最终总是以“0”结束(因为每个值 mod 1 都将是 0 :-/),然后 uptr[rid] = ...
等同于 *uptr = ...
,这实际上是正确的,因为您自己指出 uptr
始终指向有效的透明度。
现在,为什么 代码会造成这种令人困惑的 pid/rid 事情?如果我不得不从“Ray2”的命名来猜测,我会假设这个样本的不同版本可能在该 ray2 结构中使用了两条光线和两个透明度,然后使用 rid/pid 东西总是 select 右边的一对。
不过,关于“为什么这行得通”的原始问题:rid 的计算结果始终为 0,因此它总是直接写入 uptr
指向的透明度值。
我正在尝试将我的应用程序从单射线相交升级到流相交。
我不太明白教程中显示的 gather
和 scatter
functions 怎么可能起作用
该示例定义了自定义扩展光线结构 Ray2
struct Ray2
{
Ray ray;
// ray extensions
float transparency; //!< accumulated transparency value
// we remember up to 16 hits to ignore duplicate hits
unsigned int firstHit, lastHit;
unsigned int hit_geomIDs[HIT_LIST_LENGTH];
unsigned int hit_primIDs[HIT_LIST_LENGTH];
};
然后它定义了这些Ray2
结构的数组:
Ray2 primary_stream[TILE_SIZE_X*TILE_SIZE_Y];
这个数组在调用交集方法之前设置为userRayExt:
primary_context.userRayExt = &primary_stream;
rtcIntersect1M(data.g_scene,&primary_context.context,(RTCRayHit*)&primary_stream,N,sizeof(Ray2));
现在,对于 embree 与几何相交的每个光线束,都会调用过滤器回调:
/* intersection filter function for streams of general packets */
void intersectionFilterN(const RTCFilterFunctionNArguments* args)
{
int* valid = args->valid;
const IntersectContext* context = (const IntersectContext*) args->context;
struct RTCRayHitN* rayN = (struct RTCRayHitN*)args->ray;
//struct RTCHitN* hitN = args->hit;
const unsigned int N = args->N;
/* avoid crashing when debug visualizations are used */
if (context == nullptr) return;
/* iterate over all rays in ray packet */
for (unsigned int ui=0; ui<N; ui+=1)
{
/* calculate loop and execution mask */
unsigned int vi = ui+0;
if (vi>=N) continue;
/* ignore inactive rays */
if (valid[vi] != -1) continue;
/* read ray/hit from ray structure */
RTCRayHit rtc_ray = rtcGetRayHitFromRayHitN(rayN,N,ui);
Ray* ray = (Ray*)&rtc_ray;
/* calculate transparency */
Vec3fa h = ray->org + ray->dir * ray->tfar;
float T = transparencyFunction(h);
/* ignore hit if completely transparent */
if (T >= 1.0f)
valid[vi] = 0;
/* otherwise accept hit and remember transparency */
else
{
/* decode ray IDs */
const unsigned int pid = ray->id / 1;
const unsigned int rid = ray->id % 1;
Ray2* ray2 = (Ray2*) context->userRayExt;
assert(ray2);
scatter(ray2->transparency,sizeof(Ray2),pid,rid,T);
}
}
}
这个方法的最后一行我没看懂
scatter(ray2->transparency,sizeof(Ray2),pid,rid,T);
我明白它应该做什么。它应该用 T 更新对应于跟踪光线的 Ray2
的透明度 属性。但是我没有得到 why/how 这有效,因为 scatter
的实现看起来像这样:
inline void scatter(float& ptr, const unsigned int stride, const unsigned int pid, const unsigned int rid, float v) {
((float*)(((char*)&ptr) + pid*stride))[rid] = v;
}
我会重新表述这个函数以便更好地提出我的问题(但如果我没记错的话它应该是完全等价的):
inline void scatter(float& ptr, const unsigned int stride, const unsigned int pid, const unsigned int rid, float v) {
float* uptr = ((float*)(((char*)&ptr) + pid*stride));
uptr[rid] = v;
}
所以,第一行对我来说仍然有意义。构造一个指向第一个 Ray2 结构的透明字段的指针,然后递增 tid * sizeof(Ray2)
- 这是有道理的,因为它将落在另一个 transparency
字段上,因为它递增 [=28] 的倍数=]
但下一行
uptr[rid] = v;
我一点都不明白。 uptr
是一个浮点指针,指向一个透明字段。因此,除非 rid
本身是 sizeof(Ray2)
的倍数,否则它根本不会指向其中一条光线的透明场。
pid
和 rid
计算为
const unsigned int pid = ray->id / 1;
const unsigned int rid = ray->id % 1;
我觉得很奇怪。这不总是和
一样吗 const unsigned int pid = ray->id;
const unsigned int rid = 0;
?
什么是 pid
和 rid
,为什么要这样计算?
我自己没有写过这个例子,所以很难猜出它的初衷是什么,但我认为线索就在你的观察中,对于 rid 和 pid 的计算, division/modulo by '1' 没有意义。
所以,if rid 最终总是以“0”结束(因为每个值 mod 1 都将是 0 :-/),然后 uptr[rid] = ...
等同于 *uptr = ...
,这实际上是正确的,因为您自己指出 uptr
始终指向有效的透明度。
现在,为什么 代码会造成这种令人困惑的 pid/rid 事情?如果我不得不从“Ray2”的命名来猜测,我会假设这个样本的不同版本可能在该 ray2 结构中使用了两条光线和两个透明度,然后使用 rid/pid 东西总是 select 右边的一对。
不过,关于“为什么这行得通”的原始问题:rid 的计算结果始终为 0,因此它总是直接写入 uptr
指向的透明度值。