OpenCL cra sh при вызове fini sh () - PullRequest
1 голос
/ 18 января 2020

Я пишу приложение OpenCL на ma c с использованием c ++, и в некоторых случаях происходит сбой в зависимости от размера работы. Сбой программы из-за SIGABRT.

Есть ли способ получить дополнительную информацию об ошибке? Почему поднимается SIGABRT? Могу ли я его поймать?

РЕДАКТИРОВАТЬ: я понимаю, что эта программа dooz ie, однако я попытаюсь объяснить это на случай, если кто-то захочет нанести удар.

Посредством отладки я обнаружил, что причиной SIGABRT было истечение времени ожидания одного из ядер.

Программа представляет собой 3D-рендеринг на основе плиток. Это OpenCL-реализация этого алгоритма: https://github.com/ssloy/tinyrenderer

Экран разделен на плитки размером 8x8. Одно из ядер (тайлер) вычисляет, какие полигоны перекрывают каждую плитку, сохраняя результаты в структуре данных под названием tilePolys. Последующее ядро ​​(растеризатор), который запускает один рабочий элемент на плитку, выполняет итерацию по списку полисов, занимающих плитку, и растеризует их.

Сканер выполняет запись в целочисленный буфер, который представляет собой список списков полигонов индексы. Каждый список имеет фиксированный размер (polysPerTile + 1 для счетчика), где первый элемент является счетчиком, а последующие polysPerTile элементы являются индексами многоугольников в плитке. Существует один такой список на плитку.

По некоторым причинам в некоторых случаях тайлер записывает очень большое число поли (13172746) в один из списков плитки в tilePolys. Это приводит к растеризации в l oop в течение длительного времени.

Странно то, что индекс, в который записывается большое число, никогда не доступен тайлеру.

Код ядра тайлера приведен ниже:

// this kernel is executed once per polygon
// it computes which tiles are occupied by the polygon and adds the index of the polygon to the list for that tile
kernel void tiler(
        // number of polygons
        ulong nTris,
        // width of screen
        int width,
        // height of screen
        int height,
        // number of tiles in x direction
        int tilesX,
        // number of tiles in y direction
        int tilesY,
        // number of pixels per tile (tiles are square)
        int tileSize,
        // size of the polygon list for each tile
        int polysPerTile,
        // 4x4 matrix representing the viewport
        global const float4* viewport, 
        // vertex positions
        global const float* vertices,
        // indices of vertices
        global const int* indices,
        // array of array-lists of polygons per tile
        // structure of list is an int representing the number of polygons covering that tile, 
        // followed by [polysPerTile] integers representing the indices of the polygons in that tile
        // there are [tilesX*tilesY] such arraylists
        volatile global int* tilePolys)
{
    size_t faceInd = get_global_id(0);

    // compute vertex position in viewport space
    float3 vs[3];
    for(int i = 0; i < 3; i++) {
        // indices are vertex/uv/normal
        int vertInd = indices[faceInd*9+i*3];

        float4 vertHomo = (float4)(vertices[vertInd*4], vertices[vertInd*4+1], vertices[vertInd*4+2], vertices[vertInd*4+3]);

        vertHomo = vec4_mul_mat4(vertHomo, viewport);
        vs[i] = vertHomo.xyz / vertHomo.w;
    }

    float2 bboxmin = (float2)(INFINITY,INFINITY);
    float2 bboxmax = (float2)(-INFINITY,-INFINITY);

    // size of screen
    float2 clampCoords = (float2)(width-1, height-1);

    // compute bounding box of triangle in screen space
    for (int i=0; i<3; i++) {
        for (int j=0; j<2; j++) {
            bboxmin[j] = max(0.f, min(bboxmin[j], vs[i][j]));
            bboxmax[j] = min(clampCoords[j], max(bboxmax[j], vs[i][j]));
        }
    }

    // transform bounding box to tile space
    int2 tilebboxmin = (int2)(bboxmin[0] / tileSize, bboxmin[1] / tileSize);
    int2 tilebboxmax = (int2)(bboxmax[0] / tileSize, bboxmax[1] / tileSize);

    // loop over all tiles in bounding box
    for(int x = tilebboxmin[0]; x <= tilebboxmax[0]; x++) {
        for(int y = tilebboxmin[1]; y <= tilebboxmax[1]; y++) {

            // get index of tile
            int tileInd = y * tilesX + x;
            // get start index of polygon list for this tile
            int counterInd = tileInd * (polysPerTile + 1);
            // get current number of polygons in list
            int numPolys = atomic_inc(&tilePolys[counterInd]);
            // if list is full, skip tile
            if(numPolys >= polysPerTile) {
                // decrement the count because we will not add to the list
                atomic_dec(&tilePolys[counterInd]);
            } else {
                // otherwise add the poly to the list
                // the index is the offset + numPolys + 1 as tilePolys[counterInd] holds the poly count
                int ind = counterInd + numPolys + 1;
                tilePolys[ind] = (int)(faceInd);
            }   
        }
    }
}

Мои теории таковы:

  • Я неправильно реализовал функции atomi c для чтения и увеличения счетчика
  • Я использую неправильный формат чисел, приводящий к записи мусора в tilePolys
  • Одно из моих других ядер непреднамеренно записывает в tilePolys буфер

Я не думаю, что оно последнее, хотя, если вместо записи faceInd в tilePolys, я пишу постоянное значение, большое число поли исчезает.

tilePolys[counterInd+numPolys+1] = (int)(faceInd); // this is the problem line
tilePolys[counterInd+numPolys+1] = (int)(5);       // this fixes the issue

1 Ответ

3 голосов
/ 19 января 2020

Похоже, ваше ядро ​​падает на самом GPU. Вы не можете получить дополнительную диагностику напрямую, по крайней мере, в macOS. Вам нужно начать сужать проблему. Некоторые предложения:

  • Поскольку cra sh в настоящее время происходит в clFinish(), вы не знаете, какая асинхронная команда вызывает cra sh. Попробуйте перевести все вызовы в очередь в режим блокировки. Это должно привести к тому, что он вызовет sh в вызове, который на самом деле идет неправильно.
  • Проверьте коды возврата / ошибки при всех вызовах API OpenCL. Иногда игнорирование ошибки из более раннего вызова может вызвать проблемы в более позднем вызове, который зависит от более ранних результатов. Например, если создать буфер не удастся, передача результата создания этого буфера в качестве аргумента ядра вызовет проблемы при попытке запустить ядро.
  • Наиболее вероятная причина для cra sh заключается в том, что ваш OpenCL Ядро обращается к памяти за пределами или иначе использует указатели. Еще раз проверьте все вычисления индекса массива.
  • Проверьте, не возникает ли проблема с меньшими рабочими пакетами. Увеличьте масштаб из одной рабочей группы (или рабочего элемента, если группы не используются) и посмотрите, происходит ли это только за пределами определенного размера работы. Это может дать вам представление о размерах буфера и индексах массива, которые могут вызывать cra sh.
  • Систематически комментировать части вашего ядра. Если cra sh исчезнет, ​​если вы закомментируете определенный c фрагмент кода, есть большая вероятность, что проблема в этом коде.
  • Если вы сузили проблему до небольшой области кода, но не могу понять, откуда он исходит, начните записывать результаты диагностики c, чтобы проверить, что переменные имеют ожидаемые значения.

Не видя никакого кода, я не могу дайте вам более конкретный c совет, чем этот.

Обратите внимание, что OpenCL устарел в macOS, поэтому, если вы специально ориентируетесь на эту платформу и не нуждаетесь в поддержке Linux, Windows, и др c. Я рекомендую изучать Metal Compute. Apple ясно дала понять, что это платформа программирования GPU, которую они хотят поддерживать, и инструменты для нее уже намного лучше, чем когда-либо их инструменты OpenCL.

Я подозреваю, что Apple в конечном итоге прекратит реализацию поддержки OpenCL, когда они выпустить Ma c с новым типом графического процессора, так что даже если вы ориентируетесь на Ma c, а также на другие платформы, вам, вероятно, все равно придется переключиться на Metal на Ma c где-нибудь в будущем. , Начиная с macOS 10.14, минимальные системные требования ОС уже включают графический процессор с поддержкой Metal, поэтому OpenCL нужен как запасной вариант, если вы используете sh для поддержки всех моделей Ma c, способных работать с 10.13 или даже более старых версий. Версия ОС.

...