0
\$\begingroup\$

I am trying to use a compute shader for ray picking technology. My shader accepts:

  1. vertex buffer of all models
  2. index buffer of all models
  3. a buffer of transformation matrices of all models
  4. Offset buffer for specific models.

And returns a single buffer of the size of one element, storing two variables of type int.

With a small number of models, the shader works fine(<500 transformation matrices), but with a large number of models (model: 3000 vertices, 10,000 indices, 1600 transformation matrices), the computational shader freezes and reports an error: DXGI_ERROR_DEVICE_HUNG

Here is the shader code with comments:

struct Point
{
    float3 pos;
};

struct Word
{
    int index;
};

struct Transformation
{
    matrix pos;
    matrix rot;
    matrix sca;
};

struct Offset 
{
    uint countVertexAllPred;
    uint countVertex;
    uint countIndecedsAllPred;
    uint countIndeceds;
    uint countTransformations;
};

StructuredBuffer<Point> arrayPoints: register(t0); // buffer vertex
StructuredBuffer<Word> arrayIndeceds: register(t1); // buffer index
StructuredBuffer<Transformation> arrayTransformations: register(t2); // buffer transform
StructuredBuffer<Offset> arrayOffsets: register(t3); // buffer offsets


struct DataOutput
{
    int indexModel;
    int indexTransformation;
};

RWStructuredBuffer<DataOutput> outputBuffer: register(u0);


cbuffer CommonData: register(b0)
{ 
    float4 cameraPosition; 
    float4 rayPosition; 
    float4 rayDirection; 
    float4 countModel; 
};


interface TriangleMethods
{
    void createEmpty(); 
    bool isEmpty(); 
    void newTriangle(float3 x, float3 y, float3 z); 
    float3 getCenter(); 
};

class Triangle: TriangleMethods
{
    float3 a; 
    float3 b;
    float3 c;
    float3 o; 

    void createEmpty()
    {
        a = b = c = o = 0;
    }

    bool isEmpty()
    {
        float _a = a.x + a.y + a.z;
        float _b = b.x + b.y + b.z;
        float _c = c.x + c.y + c.z;
        return (_a == 0 && _b == 0 && _c == 0);
    }

    void newTriangle(float3 x, float3 y, float3 z)
    {
        a = x; b = y; c = z;
        float _x = (a.x + b.x + c.x) / 3;
        float _y = (a.y + b.y + c.y) / 3;
        float _z = (a.z + b.z + c.z) / 3;
        o = float3( _x, _y, _z );
    }

    float3 getCenter()
    {
        return o;
    }
};



bool equalsSFromCamera(Triangle t1, Triangle t2) 
{
    bool result = true;
    float length1 = length(t1.getCenter() - (float3)cameraPosition); 
    float length2 = length(t2.getCenter() - (float3)cameraPosition); 
    if(length1 < length2)
        result = false;
    return result;
}


float3 generateNormal(float3 a, float3 b, float3 c) 
{
    float3 u = b - a;
    float3 v = c - a;
    float3 p = normalize(cross(u, v));
    return p;
}


float intersectionRayAndPlane(float3 p, float3 normal)
{
    return -dot((float3)rayPosition - p, normal) / dot((float3)rayDirection, normal);
}


bool belongsPointInTriangle(float3 a, float3 b, float3 c, float3 m, float3 n)
{   
    bool result = false;
    float3 tmp;

    float3 oa = a - m;
    float3 ob = b - m;
    tmp = dot(n, cross(oa, ob));
    float v1 = tmp.x;


    float3 oc = c - m;
    tmp = dot(n, cross(ob, oc));
    float v2 = tmp.x;


    tmp = dot(n, cross(oc, oa));
    float v3 = tmp.x;


    if ((v1 < 0 && v2 < 0 && v3 < 0) || (v1 >= 0 && v2 >= 0 && v3 >= 0))
        result = true;
    return result;
}


Triangle getNearTriangleFromCamera(Triangle t1, Triangle t2)
{
    Triangle t = t2;
    float length1 = length(t1.getCenter() - (float3)cameraPosition); 
    float length2 = length(t2.getCenter() - (float3)cameraPosition); 
    if(length1 < length2)
        t = t1;
    return t;
}


[numthreads(1, 1, 1)]
void main(uint3 groupThreadID : SV_GroupThreadID)
{
    Triangle tri; 
    tri.createEmpty(); 
    DataOutput dataTriangle; 
    dataTriangle.indexModel = -1;
    dataTriangle.indexTransformation = -1;
    int indexModel = -1; 
    int indexModelMatrix = -1; 

    int offTrans = 0; 

    for(int k = 0; k <countModel.x;k++) // select every model
    {
        indexModel++;
        int start = arrayOffsets[k].countIndecedsAllPred; // select offset for current model
        int end = arrayOffsets[k].countIndeceds;
        for(int i = start;i<end;i+=3)  // select indexs current model
        {

            int i1 = (int)arrayIndeceds[i].index;
            int i2 = (int)arrayIndeceds[i+1].index;
            int i3 = (int)arrayIndeceds[i+2].index;

            // select vertex current model with gets index
            float3 p0_source = arrayPoints[i1].pos;
            float3 p1_source = arrayPoints[i2].pos;
            float3 p2_source = arrayPoints[i3].pos;

            // get count transformation for current model
            int countTransf = (int)arrayOffsets[k].countTransformations;
            for(int j = 0;j<countTransf;j++) // select transformation for current model
            {
                indexModelMatrix++;

                // mul three vertex on current matrix transformation
                float3 p0 = p0_source;
                float3 p1 = p1_source;
                float3 p2 = p2_source;

                matrix _pos = arrayTransformations[offTrans + j].pos;
                matrix _rot = arrayTransformations[offTrans + j].rot;
                matrix _sca = arrayTransformations[offTrans + j].sca;

                p0 = (float3)mul(float4(p0, 1), _sca);
                p0 = (float3)mul(float4(p0, 1), _rot);
                p0 = (float3)mul(float4(p0, 1), _pos);

                p1 = (float3)mul(float4(p1, 1), _sca);
                p1 = (float3)mul(float4(p1, 1), _rot);
                p1 = (float3)mul(float4(p1, 1), _pos);

                p2 = (float3)mul(float4(p2, 1), _sca);
                p2 = (float3)mul(float4(p2, 1), _rot);
                p2 = (float3)mul(float4(p2, 1), _pos);

               // generate normal
                float3 n = generateNormal(p0, p1, p2);


                // check ray and plane intersection
                float t = intersectionRayAndPlane(p0, n);

                // get point for ray and plane intersection 
                float3 m = (float3)rayPosition + (float3)mul(t, (float3)rayDirection);

                // check ray and triangle intersection
                if(belongsPointInTriangle(p0, p1, p2, m, n))
                {
                    Triangle triangleNew;
                    triangleNew.newTriangle(p0, p1, p2);
                    triangleNew = getNearTriangleFromCamera(tri, triangleNew); 
                    // ray hit the triangle
                    if(tri.isEmpty() || !equalsSFromCamera(tri, triangleNew)) 
                    {
                    // replace model indexes and transformation matrices    
                        dataTriangle.indexModel = indexModel;
                        dataTriangle.indexTransformation = indexModelMatrix;
                        tri = triangleNew;
                    }
                }
            }
            indexModelMatrix = -1;
        }
        offTrans += (int)arrayOffsets[k].countTransformations;
    }

    outputBuffer[0].indexModel = dataTriangle.indexModel;
    outputBuffer[0].indexTransformation = dataTriangle.indexTransformation;

}

Here is the call to the compute shader in C ++ code:

DataOutput RayPick::run(ID3D11Device* device, ID3D11DeviceContext * deviceContext)
{
    ID3D11UnorderedAccessView* nullOutBuffer(nullptr);
    ID3D11ShaderResourceView* nullResourceBuffer(nullptr);
    ID3D11Buffer* nullConstantBuffer(nullptr);

    // output buffer
    DataOutput out; out.indexModel = out.indexTransformation = -1;

    deviceContext->CSSetShader(shader, NULL, 0);
    deviceContext->CSSetConstantBuffers(0, 1, &constantBufferData); 
    deviceContext->UpdateSubresource(constantBufferData, 0, NULL, &BufferData, 0, 0);
    deviceContext->CSSetShaderResources(0, 1, &physicShaderResourceViewPoints);
    deviceContext->CSSetShaderResources(1, 1, &physicShaderResourceViewIndeceds);
    deviceContext->CSSetShaderResources(2, 1, &physicShaderResourceViewTransformation);
    deviceContext->CSSetShaderResources(3, 1, &physicShaderResourceViewOffsets);
    deviceContext->CSSetUnorderedAccessViews(0, 1, &physicShaderResourceViewOut, NULL);

    deviceContext->Dispatch(1, 1, 1);

    deviceContext->CSSetUnorderedAccessViews(0, 1, &nullOutBuffer, NULL);
    deviceContext->CSSetConstantBuffers(0, 1, &nullConstantBuffer);
    deviceContext->CSSetShaderResources(0, 1, &nullResourceBuffer);
    deviceContext->CSSetShaderResources(1, 1, &nullResourceBuffer);
    deviceContext->CSSetShaderResources(2, 1, &nullResourceBuffer);
    deviceContext->CSSetShaderResources(3, 1, &nullResourceBuffer);
    deviceContext->CSSetShader(NULL, NULL, 0);

    // get output buffer data
    deviceContext->CopyResource(outBuffer, physicBufferOut);
    D3D11_MAPPED_SUBRESOURCE mappedData;
    ZeroMemory(&mappedData, 0, sizeof(mappedData));
    HRESULT hr = deviceContext->Map(outBuffer, 0, D3D11_MAP_READ, 0, &mappedData); 

    hr = device->GetDeviceRemovedReason();
    string message;
    switch (hr)
    {
    case DXGI_ERROR_DEVICE_HUNG:
        message = "DXGI_ERROR_DEVICE_HUNG"; // bug!
        break;

    case DXGI_ERROR_DEVICE_REMOVED:
        message = "DXGI_ERROR_DEVICE_REMOVED";
        break;

    case DXGI_ERROR_DEVICE_RESET:
        message = "DXGI_ERROR_DEVICE_RESET";
        break;

    case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
        message = "DXGI_ERROR_DRIVER_INTERNAL_ERROR";
        break;

    case DXGI_ERROR_INVALID_CALL:
        message = "DXGI_ERROR_INVALID_CALL";
        break;

    case S_OK:
        message = "S_OK";
        break;

    default:
        message = "error";
        break;
    }


    if (hr != S_OK)
    {
        return out;
    }
    DataOutput* dataPtr = reinterpret_cast<DataOutput*>(mappedData.pData);
    out = *dataPtr;
    deviceContext->Unmap(outBuffer, 0);

    return out;
}

Create out and copy buffers:

// buffer for copy data
D3D11_BUFFER_DESC outputDesc;
outputDesc.Usage = D3D11_USAGE_STAGING;
outputDesc.BindFlags = 0;
outputDesc.ByteWidth = sizeof(DataOutput);
outputDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
outputDesc.StructureByteStride = sizeof(DataOutput);
outputDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
HRESULT hr = device->CreateBuffer(&outputDesc, 0, &outBuffer);
if (hr != S_OK)
    return false;

// create rw structured buffer
D3D11_BUFFER_DESC descGPUBuffer;
ZeroMemory(&descGPUBuffer, sizeof(descGPUBuffer));
descGPUBuffer.BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
descGPUBuffer.ByteWidth = sizeof(DataOutput);
descGPUBuffer.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
descGPUBuffer.StructureByteStride = sizeof(DataOutput);
hr = device->CreateBuffer(&descGPUBuffer, NULL, &physicBufferOut);
if(hr != S_OK)
    return false;

// create rw structured buffer UAV 
D3D11_BUFFER_DESC descBuf;
ZeroMemory(&descBuf, sizeof(descBuf));
physicBufferOut->GetDesc(&descBuf);
D3D11_UNORDERED_ACCESS_VIEW_DESC descView;
ZeroMemory(&descView, sizeof(descView));
descView.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
descView.Buffer.FirstElement = 0;
descView.Format = DXGI_FORMAT_UNKNOWN;
descView.Buffer.NumElements = 1;
hr = device->CreateUnorderedAccessView(physicBufferOut, &descView, &physicShaderResourceViewOut);
if(hr != S_OK)
    return false;

An error occurs at this location:

HRESULT hr = deviceContext->Map(outBuffer, 0, D3D11_MAP_READ, 0, &mappedData); 

And the debug layer also reports the following message: Removing device

While the shader is called with one group of threads and one thread (1,1,1). How to understand why this error occurs? Thank.

PS: Sorry for my bad english:(

\$\endgroup\$
4
  • \$\begingroup\$ If you're trying to map a buffer for read access, you should review your buffer creation code to verify that you've used the correct creation flags to allow this. \$\endgroup\$ Nov 15, 2018 at 13:03
  • \$\begingroup\$ @MaximusMinimus With a small number of transformation matrices (the number of models rendered on render), the data is returned correctly. \$\endgroup\$
    – Range
    Nov 15, 2018 at 13:08
  • \$\begingroup\$ Can you post your buffer creation code please? \$\endgroup\$ Nov 15, 2018 at 13:32
  • \$\begingroup\$ @MaximusMinimus yes, of course, i'am update post. \$\endgroup\$
    – Range
    Nov 15, 2018 at 13:48

1 Answer 1

1
\$\begingroup\$

This is called a Timeout Detection and Recovery (TDR) error which by default happens whenever the GPU stops responding for 2 seconds or longer--technically when a single driver packet takes longer than 2 seconds. For normal rendering, this is plenty of time for most shaders but long-running DirectCompute tasks can tie up the GPU for longer than 2 seconds.

One solution is to break up the work into smaller batches so the system stays more responsive, and general this is a good solution. You should also make sure your shader is as optimized as it can be for the workloads.

On DirectX 11.1 or later (Windows 7 SP 1 with KB 2670838, Windows 8.x, or Windows 10), you can use D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT when creating the device.

See Microsoft Docs

\$\endgroup\$
1
  • \$\begingroup\$ >> You should also make sure your shader is as optimized as it can be for the workloads. Hello. How to check it? \$\endgroup\$
    – Range
    Nov 16, 2018 at 18:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .