1
votes

I try to implement MarchingCubes algorithm on compute shader so I need to know actual number of generated triangles. I use Unreal Engine 4 and HLSL (Shader Model 5) How can I get back one single uint from shader?

I tried to generate RWStructuredBuffer with one variable and increment it with InterlockedAdd but it always returns 0 (the triangles buffer works fine).

Here is my code:

Initialization and reading in UE:

struct FVertex
{
    FVector position;
    FVector normal;
};

struct FTriangle
{
    FVertex vertices[3];
};

void MeshGeneratorShaderHelper::Initialize(const float voxel_size_mm, const size_t slices, const size_t rows, const size_t columns, const size_t cube_size)
{
    _constantParameters.voxel_size_mm = voxel_size_mm;
    _constantParameters.slices = slices;
    _constantParameters.rows = rows;
    _constantParameters.columns = columns;
    _constantParameters.cube_size = cube_size;
    _constantParameters.threshold = 0.95;


    // Setup buffers
    if (shaderOutDataBuffer != NULL)
    {
        shaderOutDataBuffer.SafeRelease();
    }
    // This is not very fine numbers
    size_t number_of_triangles = slices*rows;
    number_of_triangles = FMath::Min(10*(size_t)DEF_SIZE_VERT, number_of_triangles); 
    // Now init actual output of shader
    _meshData.Init(FTriangle(), number_of_triangles);
    TResourceArray<FTriangle> bufferData;
    bufferData.Init(FTriangle(), number_of_triangles);
    bufferData.SetAllowCPUAccess(true);
    FRHIResourceCreateInfo createInfo;
    createInfo.ResourceArray = &bufferData;

    // This is FStructuredBufferRHIRef and FUnorderedAccessViewRHIRef
    shaderOutDataBuffer = RHICreateStructuredBuffer(sizeof(FTriangle), sizeof(FTriangle) * number_of_triangles, BUF_UnorderedAccess | BUF_ShaderResource, createInfo);
    shaderOutDataBufferUAV = RHICreateUnorderedAccessView(shaderOutDataBuffer, true, false);

    // This is one element buffer for intanced triangles number
    if (sizeOutBuffer != NULL)
    {
        sizeOutBuffer.SafeRelease();
    }
    TResourceArray<uint32> bufferData2;
    bufferData2.Init(0, 1);
    bufferData2.SetAllowCPUAccess(true);
    FRHIResourceCreateInfo sizeCreateInfo;
    sizeCreateInfo.ResourceArray = &bufferData2;
    sizeOutBuffer = RHICreateStructuredBuffer(sizeof(uint32), sizeof(uint32), BUF_UnorderedAccess | BUF_ShaderResource, sizeCreateInfo);
    sizeOutBufferUAV = RHICreateUnorderedAccessView(sizeOutBuffer, true, false);

    // This is buffer for voxel input of shader
    if (voxelData != NULL)
    {
        voxelData.SafeRelease();
    }
    TResourceArray<float> bufferDataV;
    bufferDataV.Init(float(), columns*rows*slices);
    bufferDataV.SetAllowCPUAccess(true);

    FRHIResourceCreateInfo CreateInfoVoxel;
    CreateInfoVoxel.ResourceArray = &bufferDataV;
    voxelData = RHICreateStructuredBuffer(sizeof(float),
        columns*rows*slices *sizeof(float),
        EBufferUsageFlags::BUF_UnorderedAccess | BUF_ShaderResource,
        CreateInfoVoxel
    );
    voxelDataUAV = RHICreateUnorderedAccessView(voxelData, true, false);
}

void MeshGeneratorShaderHelper::RetrieveDataInternal()
{
    check(IsInRenderingThread());

    // Try to get number of triangles, always get 0
    uint32 total_size;
    uint8* srcPtr = (uint8*)RHILockStructuredBuffer(sizeOutBuffer, 0, sizeof(uint32), EResourceLockMode::RLM_ReadOnly);
    FMemory::Memcpy((uint8*)(&total_size), srcPtr, sizeof(uint32));
    RHIUnlockStructuredBuffer(sizeOutBuffer);

    if (total_size == 0)
    {
        _meshData.Empty();
        result_size = total_size;
        //return;
        total_size = DEF_SIZE_VERT;
    }

    // This works fine
    _meshData.Init(FTriangle{ {FVector{-5,-5,-5}, FVector {-5,-5,-5}, FVector {-5,-5,-5}} }, total_size);

    srcPtr = (uint8*)RHILockStructuredBuffer(shaderOutDataBuffer, 0, sizeof(FTriangle) * total_size, EResourceLockMode::RLM_ReadOnly);
    // Reference pointer to first element for our destination ComputedColors
    uint8* dstPtr = (uint8*)_meshData.GetData();
    // Copy from GPU to main memory
    FMemory::Memcpy(dstPtr, srcPtr, sizeof(FTriangle) * total_size);
    //Unlock texture
    RHIUnlockStructuredBuffer(shaderOutDataBuffer);
}

Shader code:

StructuredBuffer<float> DensityTexture; // voxel data

struct Vertex
{
    float3 Position;
    float3 Normal;
};

struct Triangle
{
    Vertex vertices[3];
};

AppendStructuredBuffer<Triangle> MeshData; // this need to be counted
RWStructuredBuffer<uint> ResultSize; // This is one num buffer

...

[numthreads(SIMULATION_BLOCK_SIZE, 1, 1)]
void MarchingCubes(
    uint3 Gid : SV_GroupID,
    uint3 DTid : SV_DispatchThreadID,
    uint3 GTid : SV_GroupThreadID,
    uint GI : SV_GroupIndex )
{
   ...
   for(uint i = 0; i<numVertsTable[cube_index]; i+=3)
        {
            Vertex vertices[3];
            for (uint j = 0; j<3; ++j)
            {
                Vertex v;
                float3 Position = vertlist[triTable[cube_index][i + j]];
                v.Normal = normalize(CalculateGradient(Position));
                v.Position = Position;
                vertices[j] = v;
            }

            Triangle t;
            t.vertices[0] = vertices[0];
            t.vertices[1] = vertices[1];
            t.vertices[2] = vertices[2];
            MeshData.Append(t);
            InterlockedAdd(ResultSize[0], 1); // Here I want to count data
        }
}
1

1 Answers

0
votes

I just did something very similar and it works, but with UE4.23

Maybe they already fixed it?

char* ShaderData = (char*)RHICmdList.LockStructuredBuffer(CounterBuffer, 0, sizeof(int32), EResourceLockMode::RLM_ReadOnly);
int32 Counter = *((int32*)ShaderData); // this is the result
RHICmdList.UnlockStructuredBuffer(CounterBuffer);