Embree: потоковый режим - как работает сбор и разброс и что такое pid и tid?
Я пытаюсь обновить свое приложение с пересечения одного луча до пересечения потока.
Я не совсем понимаю, как это возможно,
gather
и функции, показанные в обучающих программах, даже работают
В примере определяется настраиваемая расширенная структура лучей.
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 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));
теперь для каждого пучка лучей, пересекающегося с геометрией, вызывается обратный вызов фильтра:
/* 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);
Я понимаю, что ДОЛЖНО делать. Он должен обновить свойство прозрачности
Ray2
что соответствует лучу, прорисованному с помощью T. Но я не понимаю, почему и как это работает, поскольку реализация
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
поле, так как оно увеличивается на кратное
но затем следующая строка
uptr[rid] = v;
Я вообще не понимаю.
uptr
- указатель с плавающей точкой, указывающий на поле прозрачности. Так что если сам по себе не является кратным
sizeof(Ray2)
, это вообще не будет указывать на поле прозрачности одного из лучей.
и рассчитываются как
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
и почему они так вычисляются?
1 ответ
Не написав этот пример самостоятельно, трудно догадаться, каково было его первоначальное намерение, но я думаю , что разгадка заключается именно в вашем наблюдении, что для вычислений rid и pid деление/по модулю на «1» бессмысленно.
Таким образом, если rid в конечном итоге всегда оказывается равным «0» (поскольку каждое значение по модулю 1 будет равно 0 :-/), тогда
uptr[rid] = ...
эквивалентно
*uptr = ...
, что на самом деле правильно, поскольку вы сами указали, что всегда указывает на действительную прозрачность.
Теперь о том, почему код делает эту запутанную вещь pid/rid? Если бы мне пришлось угадывать по названию «Ray2», я бы предположил, что в другой версии этого образца, возможно, использовались два луча и две прозрачности в этой структуре ray2, а затем использовалась штука rid/pid, чтобы всегда выбирать правильный из пара.
Тем не менее, что касается первоначального вопроса «почему это вообще работает»: rid всегда оценивается как 0, поэтому он всегда записывает прямо в значение прозрачности, которое
uptr
указывает на.