| #version 430 | |
| 
 | |
| // Game of life transfert shader | |
| 
 | |
| #define GOL_WIDTH 768 | |
| 
 | |
| // Game Of Life Update Command | |
| // NOTE: matches the structure defined on main program | |
| struct GolUpdateCmd { | |
|     uint x;         // x coordinate of the gol command | |
|     uint y;         // y coordinate of the gol command | |
|     uint w;         // width of the filled zone | |
|     uint enabled;   // whether to enable or disable zone | |
| }; | |
| 
 | |
| // Local compute unit size | |
| layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in; | |
| 
 | |
| // Output game of life grid buffer | |
| layout(std430, binding = 1) buffer golBufferLayout | |
| { | |
|     uint golBuffer[]; // golBuffer[x, y] = golBuffer[x + GOL_WIDTH * y] | |
| }; | |
| 
 | |
| // Command buffer | |
| layout(std430, binding = 3) readonly restrict buffer golUpdateLayout | |
| { | |
|     uint count; | |
|     GolUpdateCmd commands[]; | |
| }; | |
| 
 | |
| #define isInside(x, y) (((x) >= 0) && ((y) >= 0) && ((x) < GOL_WIDTH) && ((y) < GOL_WIDTH)) | |
| #define getBufferIndex(x, y) ((x) + GOL_WIDTH * (y)) | |
| 
 | |
| void main() | |
| { | |
|     uint cmdIndex = gl_GlobalInvocationID.x; | |
|     GolUpdateCmd cmd = commands[cmdIndex]; | |
| 
 | |
|     for (uint x = cmd.x; x < (cmd.x + cmd.w); x++) | |
|     { | |
|         for (uint y = cmd.y; y < (cmd.y + cmd.w); y++) | |
|         { | |
|             if (isInside(x, y)) | |
|             { | |
|                 if (cmd.enabled != 0) atomicOr(golBuffer[getBufferIndex(x, y)], 1); | |
|                 else atomicAnd(golBuffer[getBufferIndex(x, y)], 0); | |
|             } | |
|         } | |
|     } | |
| }
 |