I have a pah-smoothing algorithm which runs post-a*. It basically gets a line between each point in the path, and based on some value interpolates for n-times. Each time it checks al the grid cells within some radious, and returns false if any are unpassable.
I need some help understanding if I can use SIMD here, and also as always any other performance tips. Because I am taking an almost 0.5 x hit in timing. Or any other LOS algorithms you may know of which you may think is better.
// Is there a line of sight between two points?
private bool LOS( int x1 , int y1 , int x2 , int y2 )
{
// Difference
int dx = x2 - x1;
int dy = y2 - y1;
// The number of steps
int n = math.max( math.abs( dx ) , math.abs( dy ) );
//UnityEngine.Debug.Log( "n " + n + " " + "dx " + dx + " " + "dy " + dy );
// The width/height of the rectange to check bounds with
float r = 1f;
// Used an int instead of a bool so I could do additions instead of branches?
// I think this is more performant
int lineOfSight = 0;
// Step along the line
for ( int step = 1; step < n; step++ )
{
// The interpolation amount
float t = step / (float) n;
float x = math.abs( math.lerp( x1 , x2 , t ) );
float y = math.abs( math.lerp( y1 , y2 , t ) );
//UnityEngine.Debug.Log("t " + t + " " + "step " + step + " " + "x " + x + " " + "y " + y );
// Convert a float rect to an int one, which will represent positions in a grid
float4 rect = new float4(
x - r ,
y - r ,
x + r ,
y + r );
int4 grid = new int4(
( int ) math.floor( rect.x ) ,
( int ) math.floor( rect.y ) ,
( int ) math.ceil( rect.z ) ,
( int ) math.ceil( rect.w ) );
// Check if any cells are unpassable
for ( int row = grid.y; row < grid.w; row++ )
{
for ( int col = grid.x; col < grid.z; col++ )
{
lineOfSight += pathNodeArray[ col + row * gridSize.x ].walkable;
}
}
}
return lineOfSight == 0;
}
There’s one algorithm I know that is very fast, but it requires walkable to be stored in a bit array padded so that rows are a multiple of a specific power of two long. The exact power of two depends on how many bytes it takes to represent the row length and whether or not you are using AVX. Padded values should be considered walkable (it’s weird, but trust me on this). Also, walkable cells should be false and non-walkable cells should be true.
Creating a bounding box around the end points.
Create an empty bit array the same dimensions as the walkable array, but clear to zero. We’ll call this the mark array.
Using a simd loop, for each cell, calculate if the distance from the cell to the line is less than 1.5 units (actually can be less if you know the grid corner overlap tolerances and slope tolerances), and if so, mark that cell in the mark array as true. Note that it is ok to spill over the left and right boundaries to get everything aligned. However, if you spill over, make sure you mask off those outside bits to zero in a postpass.
Bitwise AND the walkable bit array and the mark array over the concerning region. If any result is not 0, then the line of sight is blocked. Otherwise if all results are 0, then line of sight is achieved.
The hardest part is step 3. You will need to be clever about extracting bitmasks from simd comparisons, and combining that wwith bit reverses and shift operations to put the masks in the right spots.
Reading this, it makes sense but it makes me realize there is quite alot I don’t know. This will take me a while, but thanks. I will have to learn how to do proper SIMD loops and I don’t even know what a bit-mask is.
Just google “raytrace on 2d grid” or similar…you can optimize your algorithm quite a bit before advanced stuff…in example, first hits by google: