0
\$\begingroup\$

I have a Compute Shader which uses InterlockedMin() and InterlockedMax().

The Compute Shader works very well on my PC (AMD Rx6600) but for Android (Google Pixel 5) it doesn't calculate the atomic Min/Max, just leaves it as the original value.
Know that the Android build always uses Vulkan API.
So, how to run atomic Min/Max in Android build?

Thank you very much!

preview image compare Compute Shader in PC and Android

P.S.: For more info, I am generating terrain. atomicMin/Max is required to ensure the same result in any game session.


Updated

The staffs of Unity3D confirmed InterlockedMin/Max still works for Android. So the problem may be devices's compatibility.
For a workaround, I quit using the atomic function and instead have fixed min/max as constants. I ran my code manytimes and decided min/max would be -1.8/1.8. If I make any change in the algorithm stats I have to pre-calculate the min/max again.


Updated 2

More detail about the algorithm which I had used.

Context:

  • The map is 2D, squared
  • The height map be stored in 1D float array
  • mapSize is the edge length of the 2D map, map squared size is mapSize * mapSize

The Compute Shader

// snoise is a function to calculate procedural (simplex?) noise
// long script here: https://github.com/SebLague/Hydraulic-Erosion/blob/master/Assets/Scripts/ComputeShaders/HeightMap.compute

RWStructuredBuffer<float> heightMap;//1D aray of height map
RWStructuredBuffer<int> minMax;// index 0 is min, index 1 is max
StructuredBuffer<float2> offsets;//octaves offsets
int floatToIntMultiplier;// 1000

int mapSize;// size of an edge of the 2D map
int octaves;
float lacunarity;
float persistence;
float scaleFactor;

[numthreads(1,1,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    // get 2D position,
    int x = id.x % mapSize;
    int y = id.x / mapSize;

    float scale = scaleFactor;
    float weight = 1;
    for (int i = 0; i < octaves; i ++) {
        heightMap[id.x] += snoise(float2(x,y)/mapSize * scale + offsets[i]) * weight;
        scale *= lacunarity;
        weight *= persistence;
    }
    int val = int(heightMap[id.x] * floatToIntMultiplier);
    InterlockedMin(minMax[0],val);// calculate min here
    InterlockedMax(minMax[1],val);// calculate max here
}

The C# script

int mapSize = 1000;
int floatToIntMultiplier = 1000;
float[] heightMap = new float[ mapSize * mapSize ];

...
// set stats and dispatch the compute shader
// get calculated data from the compute shader
...

float min = minMax[0] / (float)floatToIntMultiplier ;
float max = minMax[1] / (float)floatToIntMultiplier ;
for(int i = 0; i < heightMap.Length; ++i)
{
    heightMap[i] = Mathf.InverseLerp(min, max, heightMap[i])
}
\$\endgroup\$
4
  • \$\begingroup\$ I'm not an Android/Vulkan expert, but from what I'm reading it looks like their chips/API might not support an exact 1:1 match for these instructions. I have seen some suggested ways to emulate the behaviour, but we might be able to attack the problem at the root instead by using a terrain generation algorithm that achieves deterministic output without interlocked min/max steps. If you'd like to try that, you can edit your question to show us how your current algorithm works, and ask "how to modify this into a mobile-friendly version with equivalent output?" \$\endgroup\$
    – DMGregory
    Commented Aug 24, 2023 at 12:53
  • \$\begingroup\$ hello @DMGregory, I have updated it. \$\endgroup\$
    – Suratraak
    Commented Aug 27, 2023 at 17:40
  • \$\begingroup\$ You have not included the details of your current algorithm, so we don't have the information we'd need to propose a good work-around or a way to streamline the calculation of these constants. \$\endgroup\$
    – DMGregory
    Commented Aug 27, 2023 at 17:42
  • \$\begingroup\$ hello @DMGregory , I am sorry about that. I just included the context algorithm. \$\endgroup\$
    – Suratraak
    Commented Aug 31, 2023 at 5:50

0

You must log in to answer this question.

Browse other questions tagged .