Я пытался собрать в Metal игру с падающим песком, которая использует вычислительный шейдер для выполнения этой работы. Моя первоначальная работа выглядела нормально, пока я не столкнулся со следующей проблемой:
В одном обновлении обе частицы 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;
}
}