Различное поведение между opencl c99 и c++ с одинаковой реализацией воксельного лучакастера в реальном времени

Я работаю с opencl над разработкой механизма воксельного raycasting. Я пытаюсь сделать что-то похожее на Gigavoxels от Crassin. В этой статье они используют октодерево для хранения данных вокселей. На данный момент я просто пытаюсь спуститься внутрь октодерева, пока не достигну листа, содержащего данные рендеринга.

Я сделал две реализации: одну на OpenCl на GPU и другую на C++ на CPU. Проблема, с которой я сталкиваюсь, заключается в том, что на графическом процессоре алгоритм проходит неправильное количество уровней, пока не достигнет листа внутри октодерева. Версия CPU дает правильные результаты. Алгоритм для обеих версий одинаков и код почти аналогичен.

Ребята, вы знаете, в чем может быть проблема? Это может быть аппаратная проблема, проблема с OpenCl или я что-то не так делаю? Я получаю один и тот же результат на трех разных графических процессорах nVidia.

Вот код С++:

// Calculate actual ray stepping position
glm::vec4 pos = eyeRay_o + eyeRay_d * t;

uint offset = 0;
//check if root is leaf
uint leafFlag = GetLeafBit(octreeNodes[0]);
//get children address of root
uint childrenAddress = GetChildAddress(octreeNodes[0]);

while (iterations < 30) {  
    iterations++; 

    // Calculate subdivision offset
    offset = (uint)(pos.x * 2) + (uint)(pos.y * 2) * 2 + (uint)(pos.z * 2) * 4;
     
    if (leafFlag == 1) {
         //return some colour and exit the loop
         break;
    }
    else 
    {
         glm::uvec4 off = glm::uvec4(pos.x * 2, pos.y * 2, pos.z * 2, pos.w * 2);
         pos.x = 2 * pos.x - off.x;
         pos.y = 2 * pos.y - off.y;
         pos.z = 2 * pos.z - off.z;
         pos.w = 2 * pos.w - off.w;   
    }

    // Extract node data from the children
    finalAddress = childrenAddress + offset;    
    leafFlag = GetLeafBit(nodes[finalAddress]);
    childrenAddress = GetChildAddress(nodes[finalAddress]);
}   

Вот код OpenCL:

// Calculate actual ray stepping position
float4 position = rayOrigin + rayDirection * t;
uint offset = 0;
//check if root is leaf
uint leafFlag = extractOctreeNodeLeaf(octreeNodes[0]);
//get children address of root
uint childrenAddress = extractOctreeNodeAddress(octreeNodes[0]);

//position will be in the [0, 1] interval
//size of octree is 1
while (iterations < 30) {  
    iterations++; 

    //calculate the index of the next child based on the position in the current subdivision
    offset = (uint)(position.x * 2) + (uint)(position.y * 2) * 2 + (uint)(position.z * 2) * 4;
     
    if (leafFlag == 1) {
        //return some colour and exit the loop
        break;
    }
    else 
    {
         //transform the position inside the parent 
         //to the position inside the child subdivision
         //size of child will be considered to be 1
         uint4 off; 
         off.x = floor(position.x * 2);
         off.y = floor(position.y * 2);
         off.z = floor(position.z * 2);
         off.w = floor(position.w * 2);
         position = 2 * position - off;  
    }
     
    // Extract node data from the children
    finalAddress = childrenAddress + offset; 
    leafFlag = extractOctreeNodeLeaf(octreeNodes[finalAddress]);
    //each node has an index to an array of 8 children - the index points to the first child
    childrenAddress = extractOctreeNodeAddress(octreeNodes[finalAddress]);
}

Вот ExtractOctreeNodeAddress, как и просили:

Обе функции просто выполняют некоторые битовые операции:

Версия OpenCL:

inline char extractOctreeNodeLeaf(uint value) {
 value = value >> 1;
 return value & 1;
}

inline uint extractOctreeNodeAddress(uint value) {
 return value >> 2;
}

С++ версия:

inline byte GetLeafBit(uint value)
{
 value = value >> 0x1;
 return value & 0x1;
}

inline uint GetChildAddress(uint value)
{
 return value >> 0x2;
}

Привет, нашел кое-что интересное. Я попытался вручную протестировать различные переменные, сравнивая их версии процессора и графического процессора на одном точном пикселе, положении и ориентации камеры. В приведенном ниже коде, если я запускаю программу, как сейчас, пиксель печатается белым, а значение (> 5,5 совершенно неверно по сравнению с реализацией ЦП), но если я прокомментирую последнюю структуру if и раскомментирую первую, результат, который я получаю, красный .... это немного необъяснимо для меня. Любые идеи?

if ((x == 265) && (y == 209)) {
    /*float epsilon = 0.01f;
    float4 stuff = (float4)(0.7604471f, 0.9088342f, 0.9999924f, 0);
    if(fabs(pos.x - stuff.x) < epsilon)  
        temp = (float4)(1, 0, 0, 1);
    else
        temp = (float4)(1, 1, 1, 1);

    break;*/

    if(pos.x > 5.5)
    {
        temp = (float4)(1, 1, 1, 1);
        break;
    }
}

person valeriodidonato    schedule 11.06.2012    source источник
comment
Было бы намного проще, если бы вы предоставили код.   -  person Nobody moving away from SE    schedule 11.06.2012
comment
Правильно - почти невозможно помочь, не глядя на ваш код.   -  person Ani    schedule 11.06.2012
comment
Поскольку вы говорите, что что-то не так с количеством уровней, пока лист не будет распознан, нам нужен код extractOctreeNodeLeaf. Возможно, это проблема доступа к структуре octreeNode.   -  person Nobody moving away from SE    schedule 11.06.2012
comment
Вероятно, проблема не в этом, а в следующем: 1. Почему extractOctreeNodeLeaf возвращает char из uint в uint? 2. Почему вы назначаете value = value >> 1, а не только return (value >> 1) & 1;?   -  person Nobody moving away from SE    schedule 11.06.2012
comment
1) Я этого не заметил, но, конечно, теперь, когда это исправлено, результат тот же. 2) Вот скриншот кубического объема, который я пытаюсь визуализировать. Выходной цвет — это количество итераций, где синий означает 3, а зеленый — 4. Размер октодерева — 32 вокселя, поэтому максимально допустимое количество рекурсивных подразделений — 3, потому что я останавливаюсь на кубе со стороной 8 вокселей. i46.tinypic.com/14ad2tf.png Куб должен быть полностью синим, но это это то, что я получаю. Вместо этого в реализации ЦП я ожидаю, что это будет так.   -  person valeriodidonato    schedule 11.06.2012
comment
Пробовали ли вы отлаживать с выводом промежуточных результатов, чтобы увидеть, где возникает ошибка в первую очередь? Я бы попытался сравнить процессор с графическим процессором. Это должно помочь вам найти местоположение (какую переменную) ошибки.   -  person Nobody moving away from SE    schedule 11.06.2012


Ответы (1)


Основная проблема заключалась в неявном приведении типа float4 к uint4.

Выполнение приведения поэлементно (все еще неявное) решило проблему.

person valeriodidonato    schedule 15.06.2012