Можно ли сделать так, чтобы игра с падающим песком работала параллельно на GPU? - PullRequest
0 голосов
/ 10 ноября 2018

Я пытался собрать в Metal игру с падающим песком, которая использует вычислительный шейдер для выполнения этой работы. Моя первоначальная работа выглядела нормально, пока я не столкнулся со следующей проблемой:

Sand particles

В одном обновлении обе частицы 1A и 1B могли бы течь в пространство T.

Sequential

В последовательном цикле я мог понять, как я просто переместил бы 1A в T, а затем, когда пришло время оценивать 1B, я увидел, что пространство уже заполнено.

Параллельное

Моя проблема в том, что с графическим процессором код для проверки 1A и 1B выполняется в разных потоках, поэтому в одном цикле обновления они оба могут переместиться в положение T, по существу убив один из них.

Тогда у меня вопрос, могу ли я найти параллельное решение или это можно решить только последовательно?

Вот мой код расчета, запускающий поток GPU для каждого пикселя в сетке 640 на 480:

struct Particle {
    float4 color;
    int live;
    int particleType;
};


kernel void calculateFrame(
                           device Particle *src [[buffer(1)]],
                           device Particle *dst [[buffer(2)]],
                           uint2 threadgroup_position_in_grid   [[ threadgroup_position_in_grid ]],
                           uint2 thread_position_in_threadgroup [[ thread_position_in_threadgroup ]],
                           uint2 threads_per_threadgroup        [[ threads_per_threadgroup ]]) {

    uint2 gid = threadgroup_position_in_grid;
    int index = (gid * 640) + gid;
    Particle particle = src[index];

    Particle bottom = src[toLinear( gid + uint2( 0, 1))]; //bottom
    Particle bottomLeft = src[toLinear( gid + uint2(-1, 1))]; //bottom left
    Particle bottomRight = src[toLinear( gid + uint2( 1, 1))]; //bottom right

    if (particle.live == 1 && particle.particleType == SAND) { // move down

        if (bottom.live == 0) {

            dst[index].live = 0;

            dst[toLinear( gid + uint2( 0, 1))].particleType = particle.particleType;
            dst[toLinear( gid + uint2( 0, 1))].color = particle.color;
            dst[toLinear( gid + uint2( 0, 1))].live = 1;

        }
        else if (bottomLeft.live == 0) { // move down to the left if clear
            dst[index].live = 0;

            dst[toLinear( gid + uint2( -1, 1))].particleType = particle.particleType;
            dst[toLinear( gid + uint2( -1, 1))].color = red;
            dst[toLinear( gid + uint2( -1, 1))].live = 1;


        }
        else if (bottomRight.live == 0) { // move down to the right if clear

            dst[index].live = 0;

            dst[toLinear( gid + uint2( 1, 1))].particleType = particle.particleType;
            dst[toLinear( gid + uint2( 1, 1))].color = red;
            dst[toLinear( gid + uint2( 1, 1))].live = 1;
        }
        else { // cant move
            dst[index] = src[index];
        }

    }

    if (particle.particleType == WALL) {
        dst[index] = particle;
        dst[index].live = 1;
    }

    if (particle.particleType == ERASER) {
        dst[index].color = blue;
        dst[index].live = 0;
    }
}
...