1

So I am writing a marching cubes compute shader. It’s going reasonably well and does work. I am in the processing of modifying it to remove all duplicate vertices. I want to do this in the shader. I did have a CPU function for it (using a dictionary lookup), but tbh it just isn’t quick enough for my liking - I know it can be done quicker on the GPU (if done right).

So I have a kind of similar approach. When a vertex needs to be added, it is first hashed, then that hash is used to lookup if the vertex has already been added and if so at what index of the array. I have also added detection of hash collisions so these are no longer a problem.

The issue I do have is that in a few cases (not very many, probably less than 10% but tbh I dont know), the duplicate vertices are not detected and therefore not removed. I.e. several duplicate vertices are added to the array. I think this happens when two threads both happen to be working on two duplicate vertices at the same time. So when they both check if the vertex already exists, it comes back false, so they both add the vertex, and therefore resulting in a duplicate. As I said this happens fairly rarely, but enough it still bothers me.

Here is my function for adding to the list, let me know if you need further details.

#ifndef UNIQUE_LIST
#define UNIQUE_LIST
RWStructuredBuffer<int> uniqueList_metadata;
    //0 size, 1 lastIdx
RWStructuredBuffer<int> uniqueList_indexLookup;
RWStructuredBuffer<float3> uniqueList_elements;

uint Hash(float3 v)
{
// Constants for hashing
    const uint seed = 42;
    const uint c1 = 0xcc9e2d51;
    const uint c2 = 0x1b873593;

    // Convert float3 to uint3
    uint3 intV = uint3(floor(v * 100000.0f));

    // Initial hash value
    uint hash = seed;

    // Hash the x component
    uint k1 = intV.x;
    k1 *= c1;
    k1 = (k1 << 15) | (k1 >> (32 - 15)); // ROTL32(k1, 15)
    k1 *= c2;
    hash ^= k1;
    hash = (hash << 13) | (hash >> (32 - 13)); // ROTL32(hash, 13)
    hash = hash * 5 + 0xe6546b64;

    // Hash the y component
    k1 = intV.y;
    k1 *= c1;
    k1 = (k1 << 15) | (k1 >> (32 - 15));
    k1 *= c2;
    hash ^= k1;
    hash = (hash << 13) | (hash >> (32 - 13));
    hash = hash * 5 + 0xe6546b64;

    // Hash the z component
    k1 = intV.z;
    k1 *= c1;
    k1 = (k1 << 15) | (k1 >> (32 - 15));
    k1 *= c2;
    hash ^= k1;
    hash = (hash << 13) | (hash >> (32 - 13));
    hash = hash * 5 + 0xe6546b64;

    // Finalization
    hash ^= 12; // length in bytes (3 * 4 bytes)
    hash ^= hash >> 16;
    hash *= 0x85ebca6b;
    hash ^= hash >> 13;
    hash *= 0xc2b2ae35;
    hash ^= hash >> 16;

    // Ensure bitSize output by taking modulo 2^bitSize
    int mask = (1 << uniqueList_metadata[0]) - 1;
    hash = hash & mask;

    return hash;
}

uint Rehash(uint hash)
{
    hash ^= hash >> 16;
    hash *= 0x85ebca6b;
    hash ^= hash >> 13;
    hash *= 0xc2b2ae35;
    hash ^= hash >> 16;

    int mask = (1 << uniqueList_metadata[0]) - 1;
    hash = hash & mask;
    
    return hash;
}

int UniqueList_Add(float3 element)
{
    //Get the hash value of the vertex and use it to lookup the index
    uint vecHash = Hash(element);
    int thisElementIdx = uniqueList_indexLookup[vecHash];
    
    //If the returned index is -1
    if(thisElementIdx == -1)
    {
        //Then the vertex doesn't already exist in the array and it can be added  
        InterlockedAdd(uniqueList_metadata[1], 1, thisElementIdx);
        
        int tmp;
        InterlockedExchange(uniqueList_indexLookup[vecHash], thisElementIdx, tmp);
        uniqueList_elements[thisElementIdx] = element;
    }
    else
    {
        //Else, either the vertex does already exist in the array or there is a hash collision with one that does
        //Check the proximity of the found vertex, to see if it genuinly matches, or is just a hash collision
        //Rehash the vertex hash (maximum 100 times) to find either a free hash or a genuine vertex match
        int loopLimit = 100;
        float sqrDistance = 9.99;
        do
        {
            float3 existingElement = uniqueList_elements[thisElementIdx];
            float3 diff = element - existingElement;
            sqrDistance = dot(diff, diff);
        
            if (sqrDistance < 0.0001)
            {
                //Not a collision, genuine vertex match
                //Exit the loop and the current index will be returned
                break;
            }

            vecHash = Rehash(vecHash);
            thisElementIdx = uniqueList_indexLookup[vecHash];
            
            if (thisElementIdx == -1)
            {
                //A free slot in the array has been found with the new hash (which also means the current vertex doesn't exist in the array)
                //So the vertex can be added to the array
                InterlockedAdd(uniqueList_metadata[1], 1, thisElementIdx);
                
                int tmp;
                InterlockedExchange(uniqueList_indexLookup[vecHash], thisElementIdx, tmp);
                uniqueList_elements[thisElementIdx] = element;
        
                break;
            }
        
            loopLimit--;
        } while (loopLimit > 0);
    
    }
    
    return thisElementIdx;
}

#endif

As you can I have tried to use the interlocked functions etc. to make it work.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.