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\$
  • \$\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\$ – Maximus Minimus Nov 15 '18 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 '18 at 13:08
  • \$\begingroup\$ Can you post your buffer creation code please? \$\endgroup\$ – Maximus Minimus Nov 15 '18 at 13:32
  • \$\begingroup\$ @MaximusMinimus yes, of course, i'am update post. \$\endgroup\$ – Range Nov 15 '18 at 13:48
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\$
  • \$\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 '18 at 18:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.