Embree:流模式-如何收集和分散工作,pid和tid是什么?



我正在尝试将我的应用程序从单射线交叉升级到流交叉。

我不太明白的是,教程中显示的gatherscatter函数是如何工作的

这个例子定义了一个自定义扩展射线结构体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];

这个数组在调用intersection方法之前被设置为usererrayext:

primary_context.userRayExt = &primary_stream;
rtcIntersect1M(data.g_scene,&primary_context.context,(RTCRayHit*)&primary_stream,N,sizeof(Ray2));

现在,对于每一个与几何体相交的光线束,过滤器回调被调用:

/* 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的透明度属性,但我不明白为什么/如何工作,因为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字段上,因为它是由sizeof(Ray2)的倍数递增的

然后下一行

uptr[rid] = v;
我一点也不明白。uptr是一个浮点指针,指向一个透明度字段。因此,除非rid本身是sizeof(Ray2)的倍数,否则它根本不会指向其中一条光线的透明域。

pidrid计算为

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;

?

什么是pidrid,为什么它们是这样计算的?

我自己没有写过这个例子,很难猜测它的本意是什么,但我认为线索在于你的观察,对于rid和pid计算,除以'1'的除法/模是没有意义的。

因此,如果rid最终总是以'0'结束(因为每个值mod 1将是0:-/),那么uptr[rid] = ...相当于*uptr = ...,这实际上是正确的,因为您自己指出uptr总是指向有效的透明度。

现在关于为什么代码做这个令人困惑的pid/rid的事情?如果要我从"ray2"的名字来猜的话;我会假设这个样本的不同版本可能在ray2结构体中使用两个光线和两个透明度,然后使用rid/pid来始终选择一对中的正确一个。

至于最初的问题"这到底为什么起作用?": rid总是求值为0,所以它总是直接写入uptr指向的透明度值。

最新更新