DXGI_ERROR_DEVICE_HUNG 来自 C++ AMP 上的 concurrency::copy

DXGI_ERROR_DEVICE_HUNG resulting from concurrency::copy on C++ AMP

我创建了一些 C++ AMP 代码,用于对天文图像执行背景梯度去除。它们以 RGB 的 16 位无符号整数形式出现。我的应用程序的所有处理和输出都以单精度浮点形式进行,因此我将输入数据 运行 转换为 C++ AMP 代码,然后将结果复制回 CPU(实际上图像将在被复制回之前在 GPU 上经过许多这些 C++ AMP 过滤器,但对于这个测试代码,我将它隔离到一个这样的过滤器。

一切顺利,直到我启动 concurrency::copy 操作以将数据从 GPU 阵列复制回 CPU。该操作抛出一个异常,表明 TDR 已被触发,因为 DXGI_ERROR_DEVICE_HUNG。完整的错误是:

D3D11 ERROR: ID3D11Device::RemoveDevice: Device removal has been triggered for the following reason (DXGI_ERROR_DEVICE_HUNG: The Device took an unreasonable amount of time to execute its commands, or the hardware crashed/hung. As a result, the TDR (Timeout Detection and Recovery) mechanism has been triggered. The current Device Context was executing commands when the hang occurred. The application may want to respawn and fallback to less aggressive use of the display hardware). [ EXECUTION ERROR #378: DEVICE_REMOVAL_PROCESS_AT_FAULT]

下面是有问题的代码。我省略了过滤器的代码,因为它可以很好地通过所有过滤器(我在调试器中单步执行)并且只在它复制回 CPU 时抛出异常。问题行是以下代码中的 concurrency::copy(frame, begin(cpu_frame));

array<float_3, 2> convert_input(std::vector<float_3> &output, unsigned short *input, int n, int m) {
    int o = 0;

    for (int i = 0; i < n * m * 3; i += 3) {
        output[o] = float_3((float)input[i] / (float)MAXUINT16, (float)input[i + 1] / (float)MAXUINT16, (float)input[i + 2] / (float)MAXUINT16);
        o++;
    }

    return array<float_3, 2>(n, m, begin(output));
}

void _stdcall remove_gradient(unsigned short *input, float *output, int n, int m)
{
    std::vector<float_3> cpu_frame(n * m);

    array<float_3, 2> frame = convert_input(cpu_frame, input, n, m);

    GradientRemovalFilter *filter = new GradientRemovalFilter();

    try {
        filter->FilterFrame(frame);

        concurrency::copy(frame, begin(cpu_frame));
    }
    catch (accelerator_view_removed &ex) {
        std::cout << ex.what() << std::endl;
        std::cout << ex.get_view_removed_reason() << std::endl;
    }

    for (int i = 0; i < n * m; i ++) {
        output[(i * 3)] = cpu_frame[i].r;
        output[(i * 3) + 1] = cpu_frame[i].g;
        output[(i * 3) + 2] = cpu_frame[i].b;
    }
}

知道出了什么问题以及如何预防吗?我的测试图像总共大约有 10,000 个像素,非常小,比我在现实中要使用的要小得多,所以我不明白为什么复制回来需要足够长的时间来导致 TDR 启动,尤其是当复杂的处理和向GPU的拷贝都完成得很好

上面的错误输出告诉你发生了什么:你的着色器花了很长时间,驱动程序认为 GPU 挂起。

这里的recommendations是:

  • 将您的处理分成更小的块或简化计算操作
  • 在 DirectX 11.1+ 中使用 D3D11_CREATE_DEVICE_DISABLE_GPU_TIMEOUT(参见 this post
  • 或者edit the registry延长超时时间,真的只对开发有用

编辑: 问题最有可能出现在 filter->FilterFrame 中,即 C++ AMP 代码成为可能导致 TDR 的 DirectCompute 着色器。由于 CPU/GPU synchronization/timing 差异,稍晚返回错误这一事实并不奇怪。