I am trying to use a compute shader for ray picking technology. My shader accepts:
- vertex buffer of all models
- index buffer of all models
- a buffer of transformation matrices of all models
- 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:(