SDK 指南

Nsight Aftermath SDK - 主要特性

Nsight Aftermath SDK 是一款易于使用的 C/C++ API(共享库 + 头文件),它为以下三个主要用例提供支持

  • GPU 崩溃转储创建

  • GPU 崩溃转储分析

  • 使用轻量级 GPU 事件标记进行应用程序检测

Nsight Aftermath 的 GPU 崩溃转储收集性能占用非常低,足以包含在发布的应用中 - 允许开发者挖掘关于 GPU 在野外崩溃原因的详细信息,并收集 GPU 崩溃转储以进行进一步分析(如果所需云基础设施已存在)。

使用事件标记进行应用程序检测

在其基本形式中,这通过允许程序员将标记插入 GPU 管线来实现,这些标记可以在 TDR 后读取,以确定 GPU 在故障点正在处理的工作项。Nsight Aftermath 还包含一个工具,用于查询当前的设备状态,很像传统的图形 API,但会报告更细粒度的原因。

Nsight Aftermath 的关键原则之一是使标记插入尽可能不引人注目,避免其他类似调试器常见的状况。当相关的性能成本变化足以使错误复现消失时,您可能会遇到通常所说的“海森堡错误”。Aftermath 通过使用非常轻量级的事件标记设计来避免这个问题。

然而,事件标记仍然存在相当大的性能开销,它们应仅用于开发或 QA 系统上的调试目的。因此,在某些版本的 NVIDIA 显卡驱动程序上,仅当 Nsight Aftermath GPU 崩溃转储监视器在系统上运行时,D3D11 和 D3D12 上的 Aftermath 事件标记跟踪才可用。此要求适用于 D3D12 的 R495 至 R530 驱动程序和 D3D11 的 R495+ 驱动程序。无需在监视器中进行任何 Aftermath 配置。它仅充当一个加密锁,以确保 Aftermath 事件标记不会影响最终用户系统上的应用程序性能。

对于 Vulkan,Aftermath 事件标记功能作为 Vulkan 扩展公开:NV_device_diagnostic_checkpoints

GPU 崩溃转储创建

除了基于事件标记的 GPU 工作跟踪和设备状态查询外,Nsight Aftermath 还支持创建 GPU 崩溃转储,用于深入的事后 GPU 崩溃分析。

将 GPU 崩溃转储创建集成到图形应用程序中,允许您使用已建立的 CPU 端崩溃处理工作流程和基础设施来收集和处理 GPU 崩溃转储。

GPU 崩溃转储检查和分析

开发者有两种可能性来检查和分析从启用 Nsight Aftermath GPU 崩溃转储创建功能的应用程序收集的 GPU 崩溃转储

  • 将 GPU 崩溃转储加载到 Nsight Graphics 中,并使用图形化的 GPU 崩溃转储检查器。此选项允许快速轻松地访问存储在 GPU 崩溃转储中的数据以进行可视化检查。

  • 使用 Nsight Aftermath GPU 崩溃转储解码函数以编程方式访问存储在 GPU 崩溃转储中的数据。这允许以用户定义的方式自动处理和分类 GPU 崩溃转储。

分发与支持

分发

SDK 的以下部分是可分发的:lib 目录中的所有 .dll / .so 文件。

注意:文件的重新分发受 LICENSE 文件中列出的条款和条件的约束,包括从本产品中使用的任何第三方组件继承的条款和条件。

支持

  • Microsoft Windows 10 或更高版本

  • Linux(内核 4.15.0 或更高版本)

  • D3D12、DXR、D3D11(基本支持)、Vulkan

  • NVIDIA Turing GPU

  • NVIDIA Display Driver R440 或更高版本(对于 D3D)

  • NVIDIA Display Driver R445 或更高版本(对于 Windows 上的 Vulkan)

  • NVIDIA Display Driver R455 或更高版本(对于 Linux 上的 Vulkan)

使用 Nsight Aftermath API

使用示例

在本节中,可以找到一些代码片段,这些片段展示了如何使用 Nsight Aftermath API 来收集和解码 D3D12 或 Vulkan 应用程序的崩溃转储。

代码示例涵盖以下常见的必需任务

  1. 在应用程序中启用 GPU 崩溃转储收集

  2. 配置要包含在 GPU 崩溃转储中的数据

  3. 使用 Aftermath 事件标记检测应用程序

  4. 处理 GPU 崩溃转储回调事件

  5. 处理设备移除/丢失

  6. 禁用 GPU 崩溃转储收集

  7. 使用 GPU 崩溃转储解码 API

启用 GPU 崩溃转储

应用程序通过调用 GFSDK_Aftermath_EnableGpuCrashDumps 来启用 GPU 崩溃转储创建。要使用与 GPU 崩溃转储收集相关的 Nsight Aftermath API 函数,请包含 GFSDK_Aftermath_GpuCrashDump.h 头文件。

GPU 崩溃转储收集应在应用程序创建任何 D3D12、D3D11 或 Vulkan 设备之前启用。对于在调用 GFSDK_Aftermath_EnableGpuCrashDumps 之前创建的设备相关的 GPU 崩溃或挂起,不会生成 GPU 崩溃转储。

除了启用 GPU 崩溃转储功能外,此调用还允许应用程序注册一些回调函数。首先,是一个回调函数,它将在检测到 GPU 崩溃并且崩溃转储数据可用时被调用。此回调是必需的。此外,如果着色器的调试信息可用,或者应用程序打算为异常提供额外的描述或上下文(例如崩溃时应用程序的当前状态),以便包含在 GPU 崩溃转储中,则应用程序还可以提供两个可选的回调函数。

注意

只有在启用着色器调试信息功能时,才会调用着色器调试信息回调;请参阅本文档的 配置 GPU 崩溃转储 部分中 GFSDK_Aftermath_FeatureFlags_GenerateShaderDebugInfoVK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV 的描述。

以下代码片段显示了一个示例,说明如何启用 GPU 崩溃转储以及如何设置崩溃转储通知、着色器调试信息通知、提供额外的崩溃转储描述数据以及解析应用程序管理的标记的回调。只有崩溃转储回调是强制性的。如果不需要相应的功能,其他三个回调是可选的,可以通过传递 NULL 指针来省略。在此示例中,GPU 现金转储仅为 D3D12 和 D3D11 设备启用。对于监视 Vulkan 设备,必须使用 GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan 调用 GFSDK_Aftermath_EnableGpuCrashDumps 函数。如果应用程序同时使用 D3D 和 Vulkan API,也可以组合这两个标志。

void MyApp::InitDevice()
{
    [...]

    // Enable GPU crash dumps and register callbacks.
    AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_EnableGpuCrashDumps(
        GFSDK_Aftermath_Version_API,
        GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_DX,
        GFSDK_Aftermath_GpuCrashDumpFeatureFlags_Default,   // Default behavior.
        GpuCrashDumpCallback,                               // Register callback for GPU crash dumps.
        ShaderDebugInfoCallback,                            // Register callback for shader debug information.
        CrashDumpDescriptionCallback,                       // Register callback for GPU crash dump description.
        ResolveMarkerCallback,                              // Register callback for marker resolution (R495 or later NVIDIA graphics driver).
        &m_gpuCrashDumpTracker));                           // Set the GpuCrashTracker object as user data passed back by the above callbacks.

    [...]
}

// Static wrapper for the GPU crash dump handler. See the 'Handling GPU crash dump Callbacks' section for details.
void MyApp::GpuCrashDumpCallback(const void* pGpuCrashDump, const uint32_t gpuCrashDumpSize, void* pUserData)
{
    GpuCrashTracker* pGpuCrashTracker = reinterpret_cast<GpuCrashTracker*>(pUserData);
    pGpuCrashTracker->OnCrashDump(pGpuCrashDump, gpuCrashDumpSize);
}

// Static wrapper for the shader debug information handler. See the 'Handling Shader Debug Information callbacks' section for details.
void MyApp::ShaderDebugInfoCallback(const void* pShaderDebugInfo, const uint32_t shaderDebugInfoSize, void* pUserData)
{
    GpuCrashTracker* pGpuCrashTracker = reinterpret_cast<GpuCrashTracker*>(pUserData);
    pGpuCrashTracker->OnShaderDebugInfo(pShaderDebugInfo, shaderDebugInfoSize);
}

// Static wrapper for the GPU crash dump description handler. See the 'Handling GPU Crash Dump Description Callbacks' section for details.
void MyApp::CrashDumpDescriptionCallback(PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription addDescription, void* pUserData)
{
    GpuCrashTracker* pGpuCrashTracker = reinterpret_cast<GpuCrashTracker*>(pUserData);
    pGpuCrashTracker->OnDescription(addDescription);
}

// Static wrapper for the resolve marker handler. See the 'Handling Marker Resolve Callbacks' section for details.
void MyApp::ResolveMarkerCallback(const void* pMarkerData, const uint32_t markerDataSize, void* pUserData, void** ppResolvedMarkerData, uint32_t* pResolvedMarkerDataSize)
{
    GpuCrashTracker* pGpuCrashTracker = reinterpret_cast<GpuCrashTracker*>(pUserData);
    pGpuCrashTracker->OnResolveMarker(pMarkerData, markerDataSize, ppResolvedMarkerData, pResolvedMarkerDataSize);
}

在应用程序中使用 GFSDK_Aftermath_EnableGpuCrashDumps 启用 GPU 崩溃转储将覆盖此应用程序的任何已激活 Nsight Aftermath GPU 崩溃转储监视器设置。这意味着 GPU 崩溃转储监视器不会收到与此进程相关的任何 GPU 崩溃通知,也不会为在调用该函数后创建的 D3D 或 Vulkan 设备创建任何 GPU 崩溃转储或着色器调试信息文件。此外,GPU 崩溃转储监视器中的所有配置设置都将被忽略。

配置 GPU 崩溃转储

GPU 崩溃转储中包含哪些数据由 Aftermath 每个设备的功能标志配置。配置 Aftermath 功能标志的方式在 D3D 和 Vulkan 之间有所不同。

对于 D3D,应用程序必须调用适当的 GFSDK_Aftermath_DX*_Initialize 函数来初始化所需的 Aftermath 功能标志。

此版本的 Aftermath SDK 支持 DX Aftermath 的以下功能

  • GFSDK_Aftermath_FeatureFlags_EnableMarkers - 这启用了对 DX Aftermath 事件标记的支持,包括对用户标记(由应用程序通过 GFSDK_Aftermath_SetEventMarker 显式添加)和下面更详细描述的自动调用堆栈标记的支持。

    开销注意:应仔细考虑使用事件标记(检查点)。在高频代码路径中注入标记可能会引入较高的 CPU 开销。因此,在某些版本的 NVIDIA 显卡驱动程序上,仅当 Nsight Aftermath GPU 崩溃转储监视器在系统上运行时,DX 事件标记功能才可用。此要求适用于 D3D12 的 R495 至 R530 驱动程序和 D3D11 的 R495+ 驱动程序。无需在监视器中进行任何 Aftermath 配置。它仅充当一个加密锁,以确保 Aftermath 事件标记不会影响最终用户系统上的应用程序性能。

  • GFSDK_Aftermath_FeatureFlags_CallStackCapturing - 这将指示 NVIDIA 显卡驱动程序为应用程序启动的所有绘制调用、计算和光线追踪调度、光线追踪加速结构构建操作或资源复制自动生成 Aftermath 事件标记。自动事件标记在命令列表中的相应命令之后立即添加,并将记录命令的函数的 CPU 调用堆栈作为其数据负载。这可能有助于缩小最接近导致崩溃的命令的范围。只有在也启用了事件标记功能的情况下,此功能才可用。

    开销注意:启用此功能将在命令列表记录期间导致非常高的 CPU 开销。由于固有的开销,调用堆栈捕获应仅用于开发或 QA 系统上的调试目的,而不应在运送给客户的应用程序中启用。因此,在 R495+ NVIDIA 显卡驱动程序上,仅当 Nsight Aftermath GPU 崩溃转储监视器在系统上运行时,DX 调用堆栈捕获功能才可用。无需在监视器中进行任何 Aftermath 配置。它仅充当一个加密锁,以确保 Aftermath 调用堆栈捕获不会影响最终用户系统上的应用程序性能。

注意

启用此功能后,Aftermath GPU 崩溃转储将包含崩溃应用程序的可执行文件以及它已加载的所有 DLL 的文件路径。

  • GFSDK_Aftermath_FeatureFlags_EnableResourceTracking - 此功能将启用图形驱动程序级别的实时和最近销毁的资源跟踪。这有助于用户识别与 GPU 页面错误导致的崩溃情况下看到的 GPU 虚拟地址相关的资源。由于跟踪发生在驱动程序级别,它将仅提供基本的资源信息,例如资源的大小、格式和资源对象的当前删除状态。

    开发者可能还希望使用 GFSDK_Aftermath_DX12_RegisterResource 函数注册其应用程序创建的 D3D12 资源,这将允许 Aftermath 跟踪其他信息,例如资源的调试名称。

  • GFSDK_Aftermath_FeatureFlags_GenerateShaderDebugInfo - 这指示着色器编译器为所有着色器生成调试信息(用于将传递给 NVIDIA 显卡驱动程序的着色器 DXIL 映射到着色器微代码的行表)。

    开销注意:应仔细考虑使用此选项。它可能会导致相当大的着色器编译开销以及处理相应的着色器调试信息回调的额外开销。

  • GFSDK_Aftermath_FeatureFlags_EnableShaderErrorReporting - 这会将 GPU 置于特殊模式,使其能够报告更多的运行时着色器错误。附加信息可能有助于调试由于着色器执行错误导致的渲染损坏崩溃。

注意

启用此功能不会导致任何性能开销,但可能会导致为表现出未定义行为的着色器报告更多崩溃,而这些着色器到目前为止尚未被注意到。在未启用此功能的情况下会被静默忽略的情况示例包括

  • 使用未对齐的地址访问内存,例如读取或写入不是访问大小倍数的字节地址。

  • 访问超出范围的内存,例如读取或写入超出组共享或线程局部内存的声明边界,或者从超出范围的常量缓冲区地址读取。

  • 达到调用堆栈限制。

注意

此功能仅在 R515 及更高版本的 NVIDIA 显卡驱动程序上受支持。如果应用程序在运行早期驱动程序版本的系统上运行,则将忽略此功能标志。

以下代码片段显示了如何将这些标志与 GFSDK_Aftermath_DX12_Initialize 一起使用,以启用 D3D12 设备的所有可用 Aftermath 功能。请注意,像此简单示例中那样启用所有功能可能并不总是正确的选择。应根据需求和可接受的开销来考虑启用哪些功能子集。

void MyApp::InitDevice()
{
    [...]

    D3D12CreateDevice(hardwareAdapter.Get(), D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&m_device)));

    // Initialize Nsight Aftermath for this device.
    const uint32_t aftermathFlags =
        GFSDK_Aftermath_FeatureFlags_EnableMarkers |             // Enable event marker tracking.
        GFSDK_Aftermath_FeatureFlags_CallStackCapturing |        // Enable automatic call stack event markers.
        GFSDK_Aftermath_FeatureFlags_EnableResourceTracking |    // Enable tracking of resources.
        GFSDK_Aftermath_FeatureFlags_GenerateShaderDebugInfo |   // Generate debug information for shaders.
        GFSDK_Aftermath_FeatureFlags_EnableShaderErrorReporting; // Enable additional runtime shader error reporting.

    AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_DX12_Initialize(
        GFSDK_Aftermath_Version_API,
        aftermathFlags,
        m_device.Get()));

    [...]
}

对于 Vulkan,Aftermath 功能标志在逻辑设备创建时通过 VK_NV_device_diagnostics_config 扩展配置。

支持的配置标志位是

  • VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV - 此标志将指示 NVIDIA 显卡驱动程序为应用程序启动的所有绘制调用、计算和光线追踪调度、光线追踪加速结构构建操作或资源复制自动生成诊断检查点。自动检查点在命令缓冲区中的相应命令之后立即添加,并将记录命令的函数的 CPU 调用堆栈作为其数据负载。这可能有助于缩小最接近导致崩溃的命令的范围。只有在应用程序也启用了 NV_device_diagnostic_checkpoints 扩展的情况下,此配置标志才有效。

    开销注意:应仔细考虑使用此标志。启用调用堆栈捕获可能会在命令缓冲区记录期间导致相当大的 CPU 开销。

注意

启用此功能后,Aftermath GPU 崩溃转储将包含崩溃应用程序的可执行文件以及它已加载的所有 DLL 或 DSO 的文件路径。

  • VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV - 此标志将启用图形驱动程序级别的实时和最近销毁的资源跟踪。这有助于用户识别与 GPU 页面错误导致的崩溃情况下看到的 GPU 虚拟地址相关的资源。

    正在跟踪的信息包括资源的大小、格式和资源对象的当前删除状态,以及通过 vkSetDebugUtilsObjectNameEXT 设置的资源的调试名称。

  • VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV - 这指示着色器编译器生成调试信息(行表)。从 SPIR-V 代码到着色器微代码的映射还是从着色器的源代码到着色器微代码的映射取决于发送到 NVIDIA 显卡驱动程序的 SPIR-V 代码是否使用调试信息编译。

    开销注意:应仔细考虑使用此选项。它可能会导致相当大的着色器编译开销以及处理相应的着色器调试信息回调的额外开销。

  • VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_ERROR_REPORTING_BIT_NV - 此标志将 GPU 置于特殊模式,使其能够报告更多的运行时着色器错误。附加信息可能有助于调试由于着色器执行错误导致的渲染损坏或崩溃。

注意

启用此功能不会导致任何性能开销,但可能会导致为表现出未定义行为的着色器报告更多崩溃,而这些着色器到目前为止尚未被注意到。在未启用此功能的情况下会被静默忽略的情况示例包括

  • 使用未对齐的地址访问内存,例如读取或写入不是访问大小倍数的字节地址。

  • 访问超出范围的内存,例如读取或写入超出共享或线程局部内存的边界,或者从超出范围的常量缓冲区地址读取。

  • 使用不兼容的格式或内存布局访问纹理。

  • 达到调用堆栈限制。

注意

此功能仅在 R515 及更高版本的 NVIDIA 显卡驱动程序上受支持。如果应用程序在运行早期驱动程序版本的系统上运行,则将忽略此功能标志。

Vulkan 设备的 Aftermath 功能选择可能类似于以下代码。请注意,像此简单示例中那样启用所有功能可能并不总是正确的选择。应根据需求和可接受的开销来考虑启用哪些功能子集。

void MyApp::InitDevice()
{
    std::vector<char const*> extensionNames;

    [...]

    // Enable NV_device_diagnostic_checkpoints extension to be able to
    // use Aftermath event markers.
    extensionNames.push_back(VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME);

    // Enable NV_device_diagnostics_config extension to configure Aftermath
    // features.
    extensionNames.push_back(VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME);

    // Set up device creation info for Aftermath feature flag configuration.
    VkDeviceDiagnosticsConfigFlagsNV aftermathFlags =
        VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV |  // Enable automatic call stack checkpoints.
        VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV |      // Enable tracking of resources.
        VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV |      // Generate debug information for shaders.
        VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_ERROR_REPORTING_BIT_NV;  // Enable additional runtime shader error reporting.

    VkDeviceDiagnosticsConfigCreateInfoNV aftermathInfo = {};
    aftermathInfo.sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV;
    aftermathInfo.flags = aftermathFlags;

    // Set up device creation info.
    VkDeviceCreateInfo deviceInfo = {};
    deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    deviceInfo.pNext = &aftermathInfo;
    deviceInfo.queueCreateInfoCount = 1;
    deviceInfo.pQueueCreateInfos = &queueInfo;
    deviceInfo.enabledExtensionCount = extensionNames.size();
    deviceInfo.ppEnabledExtensionNames = extensionNames.data();

    // Create the logical device.
    VkDevice device;
    vkCreateDevice(physicalDevice, &deviceInfo, NULL, &device);

    [...]
}

插入事件标记

Aftermath API 提供了一个简单且轻量级的解决方案,用于在 GPU 时间线上插入事件标记。

以下是一些 D3D12 示例代码,展示了如何使用 GFSDK_Aftermath_DX12_CreateContextHandle 创建必要的命令上下文句柄,以及如何调用 GFSDK_Aftermath_SetEventMarker 来设置一个简单的事件标记,并将字符串作为负载。

注意

传递到 GFSDK_Aftermath_DX12_CreateContextHandle 的命令列表必须处于记录状态,返回的上下文句柄才有效。即使命令列表随后被关闭和重置,上下文句柄仍然有效,但是当其上下文句柄传递到 GFSDK_Aftermath_SetEventMarker 时,命令列表也必须处于记录状态。

注意

GFSDK_Aftermath_SetEventMarker 的调用只有在为 GFSDK_Aftermath_DX12_Initialize 提供了 GFSDK_Aftermath_FeatureFlags_EnableMarkers 选项并且 Nsight Aftermath GPU 崩溃转储监视器在系统上运行时才有效。此监视器要求适用于 D3D12 的 R495 至 R530 驱动程序和 D3D11 的 R495+ 驱动程序。

void MyApp::PopulateCommandList()
{
    // Create the command list.
    m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), m_pipelineState.Get(), IID_PPV_ARGS(&m_commandList)));

    // Create an Nsight Aftermath context handle for setting Aftermath event markers in this command list.
    // Note that the command list must be in the recording state for this function, so if it is closed it must be reset first
    // (e.g. if it was created above with ID3D12Device4::CreateCommandList1 instead of ID3D12Device::CreateCommandList).
    AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_DX12_CreateContextHandle(m_commandList.Get(), &m_hAftermathCommandListContext));

    [...]

    // Add an Aftermath event marker with a 0-terminated string as payload.
    std::string eventMarker = "Draw Triangle";
    AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_SetEventMarker(m_hAftermathCommandListContext, (void*)eventMarker.c_str(), (unsigned int)eventMarker.size() + 1));
    m_commandList->DrawInstanced(3, 1, 0, 0);

    [...]
}

开销注意:为了减少 CPU 开销,请使用 dataSize=0GFSDK_Aftermath_SetEventMarker。这指示 Aftermath 不要内部分配和复制内存,而是依赖应用程序自行管理标记指针。以这种方式管理的标记稍后可以使用 Aftermath SDK 解析标记回调 PFN_GFSDK_Aftermath_ResolveMarkerCb 来解析。

对于 Vulkan,类似的功能通过 NV_device_diagnostic_checkpoints 扩展提供。当为 Vulkan 设备启用此扩展时,可以使用 vkCmdSetCheckpointNV 函数在命令缓冲区中插入事件标记。应用程序负责管理标记指针/令牌,这些指针/令牌稍后可以使用 Aftermath SDK 解析标记回调 PFN_GFSDK_Aftermath_ResolveMarkerCb 来解析。

std::map<uint64_t, std::string> appManagedMarkers;

void MyApp::RecordCommandBuffer()
{
    [...]
    size_t markerId = appManagedMarkers.size() + 1;
    appManagedMarkers[markerId] = "Draw Cube";
    // Add an Aftermath event marker ID, which requires resolution later via callback.
    vkCmdSetCheckpointNV(commandBuffer, (const void*)markerId);
    [...]
}

处理 GPU 崩溃转储回调

当启用 Nsight Aftermath GPU 崩溃转储并且检测到 GPU 崩溃或挂起时,将收集必要的数据,并从中创建 GPU 崩溃转储。然后将调用使用 GFSDK_Aftermath_EnableGpuCrashDumps 注册的 GPU 崩溃转储回调函数,以通知应用程序。在回调中,应用程序可以使用 GPU 崩溃转储解码 API 解码 GPU 崩溃转储数据,或者将其转发到崩溃处理基础设施。

在下面显示的 GPU 崩溃转储回调处理程序的简单示例实现中,GPU 崩溃转储数据只是存储到一个文件中。然后可以打开此文件以使用 Nsight Graphics 进行分析。

// Handler for GPU crash dump callbacks (called by GpuCrashDumpCallback).
void GpuCrashTracker::OnCrashDump(const void* pGpuCrashDump, const uint32_t gpuCrashDumpSize)
{
    // Make sure only one thread at a time...
    std::lock_guard<std::mutex> lock(m_mutex);

    // Write to file for later in-depth analysis.
    WriteGpuCrashDumpToFile(pGpuCrashDump, gpuCrashDumpSize);
}

注意

所有回调函数都是自由线程的,应用程序负责提供线程安全的回调处理程序。

重要的是要注意,应用程序使用的图形 API 的设备移除/丢失通知与 NVIDIA 显卡驱动程序的 GPU 崩溃处理是异步的。这意味着应用程序应检查 IDXGISwapChain::Present 的返回值是否为 DXGI_ERROR_DEVICE_REMOVEDDXGI_ERROR_DEVICE_RESET,以及其 Vulkan 调用的返回代码是否为 VK_ERROR_DEVICE_LOST,并在释放 D3D 或 Vulkan 设备或终止进程之前,给 Nsight Aftermath GPU 崩溃转储处理线程一些时间来完成其工作。有关更多详细信息,请参阅本文档的“处理设备移除/丢失”部分。

处理 GPU 崩溃转储描述回调

应用程序可以注册一个可选的回调函数,该函数允许它提供关于崩溃的补充信息。此回调在 GPU 崩溃发生之后但在实际 GPU 崩溃转储回调之前调用。这为应用程序提供了提供诸如应用程序名称、应用程序版本或用户定义的数据(例如,当前引擎状态)等信息的机会。所提供的数据将存储在 GPU 崩溃转储中。

以下是基本 GPU 崩溃转储描述处理程序的示例。通过调用回调提供的 addDescription 函数将数据添加到崩溃转储中。

// Handler for GPU crash dump description callbacks (called by CrashDumpDescriptionCallback).
void GpuCrashTracker::OnDescription(PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription addDescription)
{
    // Add some basic description about the crash.
    addDescription(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "Hello Nsight Aftermath");
    addDescription(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationVersion, "v1.0");
    addDescription(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_UserDefined, "This is a GPU crash dump example");
    addDescription(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_UserDefined + 1, "Engine State: Rendering");
}

注意

所有回调函数都是自由线程的;应用程序负责提供线程安全的回调处理程序。

处理着色器调试信息回调

如果设备配置了 GenerateShaderDebugInfo 功能标志,则生成的着色器调试信息将通过在调用 GFSDK_Aftermath_EnableGpuCrashDumps 时注册的(可选)着色器调试信息回调函数传达给应用程序。在 Nsight Graphics 中分析崩溃转储或使用 Nsight Aftermath API 的 GPU 崩溃转储到 JSON 解码函数时,将需要此调试信息来从着色器指令地址映射到中间汇编语言 (IL) 指令或高级源代码行。如果不需要此功能,应用程序可以在配置设备时省略 GenerateShaderDebugInfo 标志,并为着色器调试信息回调传递 nullptr。这可能是可取的,因为生成着色器调试信息会在着色器编译和处理回调方面产生开销。

以下是一个回调处理程序的简单示例实现,该处理程序使用从不透明着色器调试信息 blob 查询的唯一着色器调试信息标识符将数据写入磁盘。

// Handler for shader debug information callbacks (called by ShaderDebugInfoCallback)
void GpuCrashTracker::OnShaderDebugInfo(const void* pShaderDebugInfo, const uint32_t shaderDebugInfoSize)
{
    // Make sure only one thread at a time...
    std::lock_guard<std::mutex> lock(m_mutex);

    // Get shader debug information identifier.
    GFSDK_Aftermath_ShaderDebugInfoIdentifier identifier = {};
    AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GetShaderDebugInfoIdentifier(GFSDK_Aftermath_Version_API, pShaderDebugInfo, shaderDebugInfoSize, &identifier));

    // Write to file for later in-depth analysis of crash dumps with Nsight Graphics.
    WriteShaderDebugInformationToFile(identifier, pShaderDebugInfo, shaderDebugInfoSize);
}

默认情况下,将为 NVIDIA 显卡驱动程序编译的每个着色器调用着色器调试信息回调。实现的任务是处理这些回调并在发生 GPU 崩溃时存储数据。为了简化该过程,Nsight Aftermath 库可以处理调试信息的缓存,并且仅在发生 GPU 崩溃的情况下以及仅针对相应 GPU 崩溃转储中引用的着色器调用回调。通过在启用 GPU 崩溃转储时将 GFSDK_Aftermath_GpuCrashDumpFeatureFlags_DeferDebugInfoCallbacks 标志传递给 GFSDK_Aftermath_EnableGpuCrashDumps 来启用此行为。

注意

所有回调函数都是自由线程的;应用程序负责提供线程安全的回调处理程序。

处理标记解析回调

注意,此 Aftermath 功能仅在 R495 及更高版本的 NVIDIA 显卡驱动程序上受支持。

对于使用 markerSize 为零调用 GFSDK_Aftermath_SetEventMarker 的 D3D 应用程序,或对于所有 Vulkan 应用程序,应用程序将唯一令牌(例如指针)传递给 Aftermath 以标识标记,并负责在内部跟踪该令牌的含义。生成崩溃转储时,如果感兴趣的标记的大小为零,则崩溃转储过程将调用此回调,传回标记的令牌,期望应用程序将令牌解析为实际的标记数据(例如字符串)并将数据传回。

令牌到标记数据的关联完全取决于应用程序。指向应用程序管理的标记数据的指针通常是一个可行的令牌,因为如果应用程序将所有标记数据保存在内存中,则不同的标记将根据定义具有唯一的指针值。

从应用程序传回给崩溃转储过程的标记数据指针必须在下次调用 ResolveMarkerCallback 之前或在崩溃转储生成期间有效。当调用 GPU 崩溃转储回调时,崩溃转储生成完成。

如果应用程序未实现此回调,或者未传回已解析的标记数据和大小,则原始标记负载(或仅零大小/Vulkan 标记的标记地址)将添加到崩溃转储中。如果 NVIDIA 显卡驱动程序不支持该功能,即如果驱动程序发布版本低于 R495,则也会发生同样的情况。

如果标记仍然被认为是有效的,但应用程序没有关联的负载(负载生命周期已过期,标记是负载等),您可以考虑在回调实现中返回标记的字符串表示形式。

// Simple data structure used to store marker tokens. This is managed by the application.
// It could be stored within the callbacks pUserData or in some other scope.
std::map<uint64_t, std::string> appManagedMarkers;

// Example handler for resolving markers (called by ResolveMarkerCallback)
void GpuCrashTracker::OnResolveMarker(const void* pMarkerData, const uint32_t markerDataSize, void** ppResolvedMarkerData, uint32_t* pResolvedMarkerDataSize)
{
    // In this example, we set the (uint64_t) key of the 'appManagedMarkers' to the 'markerData'
    // and set the 'markerDataSize' to zero when we call the 'GFSDK_Aftermath_SetEventMarker'.
    // So we are only interested in zero size markers here.
    if (markerDataSize == 0)
    {
        // Important: the pointer passed back via ppResolvedMarkerData must remain valid after this function returns.
        // Using references for all of the appManagedMarkers accesses ensures that the pointers refer to the persistent data
        const auto& foundMarker = appManagedMarkers->find((uint64_t)pMarkerData);
        if (foundMarker != appManagedMarkers->end())
        {
            const std::string& markerString = foundMarker->second;
            // std::string::data() will return a valid pointer until the string is next modified
            // we don't modify the string after calling data() here, so the pointer should remain valid
            *ppResolvedMarkerData = (void*)markerString.data();
            *pResolvedMarkerDataSize = (uint32_t)markerString.length();
            return;
        }
    }
    // Marker was not found or we are not interested in it. So we return without setting any resolved
    // marker data or size. The marker's original payload (or the marker's pointer/token for zero-size
    // marker) will be stored in the Aftermath crash dump.
    // When capturing lots of markers, it may be unfeasible to to maintain the lifetime of all markers.
    // It may be desirable to maintain only markers recorded in the most recent render calls.
}

处理设备移除/丢失

在 GPU 崩溃发生后(例如,在应用程序中看到设备移除/丢失后),调用 GFSDK_Aftermath_GetCrashDumpStatus 以检查 GPU 崩溃转储状态。应用程序应等待 Aftermath 完成崩溃转储处理,然后再退出/终止应用程序。以下是应用程序在设置为收集 Aftermath GPU 崩溃转储时如何处理设备移除事件的示例。

void Graphics::Present(void)
{
    [...]

    HRESULT hr = s_SwapChain1->Present(PresentInterval, 0);

    if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET)
    {
        [...]

        GFSDK_Aftermath_CrashDump_Status status = GFSDK_Aftermath_CrashDump_Status_Unknown;
        AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GetCrashDumpStatus(&status));

        auto tStart = std::chrono::steady_clock::now();
        auto tElapsed = std::chrono::milliseconds::zero();

        // Loop while Aftermath crash dump data collection has not finished or
        // the application is still processing the crash dump data.
        while (status != GFSDK_Aftermath_CrashDump_Status_CollectingDataFailed &&
               status != GFSDK_Aftermath_CrashDump_Status_Finished &&
               tElapsed.count() < deviceLostTimeout)
        {
            // Sleep a couple of milliseconds and poll the status again.
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GetCrashDumpStatus(&status));

            tElapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - tStart);
        }

        if (status == GFSDK_Aftermath_CrashDump_Status_Finished)
        {
            Utility::Print("Aftermath finished processing the crash dump.\n");
        }
        else
        {
            Utility::Printf("Unexpected crash dump status after timeout: %d\n", status);
        }

        exit(-1);
    }

    [...]
}

禁用 GPU 崩溃转储

要禁用 GPU 崩溃转储,只需调用 GFSDK_Aftermath_DisableGpuCrashDumps

MyApp::~MyApp()
{
    [...]

    // Disable GPU crash dump creation.
    GFSDK_Aftermath_DisableGpuCrashDumps();

    [...]
}

在应用程序中使用 GFSDK_Aftermath_DisableGpuCrashDumps 禁用 GPU 崩溃转储将重新启用此应用程序的任何 Nsight Aftermath GPU 崩溃转储监视器设置(如果它在系统上运行)。在此之后,GPU 崩溃转储监视器将收到与此进程相关的任何 GPU 崩溃通知,并将为所有活动设备创建 GPU 崩溃转储和着色器调试信息文件。

如何读取崩溃转储

Nsight Aftermath 库提供了几个函数,用于解码 GPU 崩溃转储和查询崩溃转储中的数据。要使用这些函数,请包含 GFSD_Aftermath_GpuCrashDumpDecoding.h 头文件。

解码 GPU 崩溃转储的第一步是通过调用 GFSDK_Aftermath_GpuCrashDump_CreateDecoder 为其创建解码器对象

// Create a GPU crash dump decoder object for the GPU crash dump.
GFSDK_Aftermath_GpuCrashDump_Decoder decoder = {};
AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_CreateDecoder(
    GFSDK_Aftermath_Version_API,
    pGpuCrashDump,
    gpuCrashDumpSize,
    &decoder));

然后,可以使用一个或多个解码器函数从 GPU 崩溃转储中读取数据。GPU 崩溃转储中的数据因 GPU 崩溃的类型和初始化 Aftermath 时使用的功能标志而异。例如,如果应用程序不使用 Aftermath 事件标记,则查询 Aftermath 事件标记信息将失败。如果请求的数据不可用,解码器将返回 GFSDK_Aftermath_Result_NotAvailable。实现应意识到这一点并相应地处理这种情况。

以下是如何使用先前创建的解码器对象从 GPU 崩溃转储中查询 GPU 页面错误信息的示例

// Query GPU page fault information.
GFSDK_Aftermath_GpuCrashDump_PageFaultInfo pageFaultInfo = {};
GFSDK_Aftermath_Result result = GFSDK_Aftermath_GpuCrashDump_GetPageFaultInfo(decoder, &pageFaultInfo);

if (GFSDK_Aftermath_SUCCEED(result) && result != GFSDK_Aftermath_Result_NotAvailable)
{
    // Print information about the GPU page fault.
    Utility::Printf("GPU page fault at 0x%016llx", pageFaultInfo.faultingGpuVA);
    Utility::Printf("Fault Type: %u", pageFaultInfo.faultType);
    Utility::Printf("Access Type: %u", pageFaultInfo.accessType);
    Utility::Printf("Engine: %u", pageFaultInfo.engine);
    Utility::Printf("Client: %u", pageFaultInfo.client);
    if (pageFaultInfo.resourceInfoCount > 0)
    {
        std::vector<GFSDK_Aftermath_GpuCrashDump_ResourceInfo> resourceInfos(pageFaultInfo.resourceInfoCount);
        GFSDK_Aftermath_GpuCrashDump_GetPageFaultResourceInfo(decoder, pageFaultInfo.resourceInfoCount, resourceInfos.data());

        int index = 0;
        for (auto resourceInfo : resourceInfos)
        {
            Utility::Printf("Resource[%d]", index);
            Utility::Printf("\tFault in resource starting at 0x%016llx", resourceInfo.gpuVa);
            Utility::Printf("\tSize of resource: (w x h x d x ml) = {%u, %u, %u, %u} = %llu bytes",
                resourceInfo.width,
                resourceInfo.height,
                resourceInfo.depth,
                resourceInfo.mipLevels,
                resourceInfo.size);
            Utility::Printf("\tFormat of resource: %u", resourceInfo.format);
            Utility::Printf("\tResource was destroyed: %d", resourceInfo.bWasDestroyed);
            index++;
        }
    }
}

某些解码函数要求调用者为它们返回的数据提供适当大小的缓冲区。这些函数附带一个额外的函数来查询缓冲区的大小。例如,查询 GPU 崩溃或挂起时所有活动的着色器可能如下所示

// First query active shaders count.
uint32_t shaderCount = 0;
GFSDK_Aftermath_Result result = GFSDK_Aftermath_GpuCrashDump_GetActiveShadersInfoCount(decoder, &shaderCount);

if (GFSDK_Aftermath_SUCCEED(result) && result != GFSDK_Aftermath_Result_NotAvailable)
{
    // Allocate buffer for results.
    std::vector<GFSDK_Aftermath_GpuCrashDump_ShaderInfo> shaderInfos(shaderCount);

    // Query active shaders information.
    result = GFSDK_Aftermath_GpuCrashDump_GetActiveShadersInfo(decoder, shaderCount, shaderInfos.data());

    if (GFSDK_Aftermath_SUCCEED(result))
    {
        // Print information for each active shader
        for (const GFSDK_Aftermath_GpuCrashDump_ShaderInfo& shaderInfo : shaderInfos)
        {
            Utility::Printf("Active shader: ShaderHash = 0x%016llx ShaderInstance = 0x%016llx Shadertype = %u",
                shaderInfo.shaderHash,
                shaderInfo.shaderInstance,
                shaderInfo.shaderType);
        }
    }
}

最后,GPU 崩溃转储解码器还提供了将 GPU 崩溃转储转换为 JSON 格式的函数。以下是从 GPU 崩溃转储创建 JSON 的代码示例,包括有关 GPU 崩溃或挂起时所有活动着色器的信息。该信息还包括相应的活动着色器 Warp,包括它们到中间汇编语言 (IL) 指令或源的映射(如果可用)。后者要求调用者还提供几个回调函数,解码器将调用这些回调函数以查询着色器调试信息和着色器二进制文件(dxc 着色器对象输出或 SPIR-V 着色器文件)。这些是可选的,对将着色器指令地址映射到 IL 或源代码行不感兴趣的实现可以简单地传递 nullptr。但是,如果需要着色器指令映射,则实现需要确保它可以向解码器提供必要的信息。

// Flags controlling what to include in the JSON data
const uint32_t jsonDecoderFlags =
    GFSDK_Aftermath_GpuCrashDumpDecoderFlags_SHADER_INFO |          // Include information about active shaders.
    GFSDK_Aftermath_GpuCrashDumpDecoderFlags_WARP_STATE_INFO |      // Include information about active shader warps.
    GFSDK_Aftermath_GpuCrashDumpDecoderFlags_SHADER_MAPPING_INFO;   // Try to map shader instruction addresses to shader lines.

// Query the size of the required results buffer
uint32_t jsonSize = 0;
GFSDK_Aftermath_Result result = GFSDK_Aftermath_GpuCrashDump_GenerateJSON(
    decoder,
    jsonDecoderFlags,                                            // The flags controlling what information to include in the JSON.
    GFSDK_Aftermath_GpuCrashDumpFormatterFlags_CONDENSED_OUTPUT, // Generate condensed output, i.e., omit all unnecessary whitespace.
    ShaderDebugInfoLookupCallback,                               // Callback function invoked to find shader debug information data.
    ShaderLookupCallback,                                        // Callback function invoked to find shader binary data by shader hash.
    ShaderSourceDebugDataLookupCallback,                         // Callback function invoked to find shader source debug data by shader DebugName.
    &m_gpuCrashDumpTracker,                                      // User data that will be provided to the above callback functions.
    &jsonSize);                                                  // Result of the call: size in bytes of the generated JSON data.

if (GFSDK_Aftermath_SUCCEED(result) && result != GFSDK_Aftermath_Result_NotAvailable)
{
    // Allocate buffer for results.
    std::vector<char> json(jsonSize);

    // Query the generated JSON data taht si cached inside the decoder object.
    result = GFSDK_Aftermath_GpuCrashDump_GetJSON(
        decoder,
        json.size(),
        json.data());
    if (GFSDK_Aftermath_SUCCEED(result))
    {
        Utility::Printf("JSON: %s", json.data());
    }
}

着色器调试信息和着色器二进制查找回调的可能实现

// Static callback wrapper for OnShaderDebugInfoLookup
void MyApp::ShaderDebugInfoLookupCallback(
    const GFSDK_Aftermath_ShaderDebugInfoIdentifier* pIdentifier,
    PFN_GFSDK_Aftermath_SetData setShaderDebugInfo,
    void* pUserData)
{
    GpuCrashTracker* pGpuCrashTracker = reinterpret_cast<GpuCrashTracker*>(pUserData);
    pGpuCrashTracker->OnShaderDebugInfoLookup(*pIdentifier, setShaderDebugInfo);
}

// Static callback wrapper for OnShaderLookup
void MyApp::ShaderLookupCallback(
    const GFSDK_Aftermath_ShaderBinaryHash* pShaderHash,
    PFN_GFSDK_Aftermath_SetData setShaderBinary,
    void* pUserData)
{
    GpuCrashTracker* pGpuCrashTracker = reinterpret_cast<GpuCrashTracker*>(pUserData);
    pGpuCrashTracker->OnShaderLookup(*pShaderHash, setShaderBinary);
}

// Static callback wrapper for OnShaderSourceDebugInfoLookup
void MyApp::ShaderSourceDebugInfoLookupCallback(
    const GFSDK_Aftermath_ShaderDebugName* pShaderDebugName,
    PFN_GFSDK_Aftermath_SetData setShaderBinary,
    void* pUserData)
{
    GpuCrashTracker* pGpuCrashTracker = reinterpret_cast<GpuCrashTracker*>(pUserData);
    pGpuCrashTracker->OnShaderSourceDebugInfoLookup(*pShaderDebugName, setShaderBinary);
}

// Handler for shader debug information lookup callbacks.
// This is used by the JSON decoder for mapping shader instruction
// addresses to IL lines or source lines.
void GpuCrashTracker::OnShaderDebugInfoLookup(
    const GFSDK_Aftermath_ShaderDebugInfoIdentifier& identifier,
    PFN_GFSDK_Aftermath_SetData setShaderDebugInfo) const
{
    // Search the list of shader debug information blobs received earlier.
    auto i_debugInfo = m_shaderDebugInfo.find(identifier);
    if (i_debugInfo == m_shaderDebugInfo.end())
    {
        // Early exit, nothing found. No need to call setShaderDebugInfo.
        return;
    }

    // Let the GPU crash dump decoder know about the shader debug information
    // that was found.
    setShaderDebugInfo(i_debugInfo->second.data(), i_debugInfo->second.size());
}

// Handler for shader lookup callbacks.
// This is used by the JSON decoder for mapping shader instruction
// addresses to IL lines or source lines.
// NOTE: If the application loads stripped shader binaries, Aftermath
// will require access to both the stripped and the non-stripped
// shader binaries.
void GpuCrashTracker::OnShaderLookup(
    const GFSDK_Aftermath_ShaderBinaryHash& shaderHash,
    PFN_GFSDK_Aftermath_SetData setShaderBinary) const
{
    // Find shader binary data for the shader hash in the shader database.
    std::vector<uint8_t> shaderBinary;
    if (!m_shaderDatabase.FindShaderBinary(shaderHash, shaderBinary))
    {
        // Early exit, nothing found. No need to call setShaderBinary.
        return;
    }

    // Let the GPU crash dump decoder know about the shader data
    // that was found.
    setShaderBinary(shaderBinary.data(), shaderBinary.size());
}

// Handler for shader source debug info lookup callbacks.
// This is used by the JSON decoder for mapping shader instruction addresses to
// source lines if the shaders used by the application were compiled with
// separate debug info data files.
void GpuCrashTracker::OnShaderSourceDebugInfoLookup(
    const GFSDK_Aftermath_ShaderDebugName& shaderDebugName,
    PFN_GFSDK_Aftermath_SetData setShaderBinary) const
{
    // Find source debug info for the shader DebugName in the shader database.
    std::vector<uint8_t> sourceDebugInfo;
    if (!m_shaderDatabase.FindSourceShaderDebugData(shaderDebugName, sourceDebugInfo))
    {
        // Early exit, nothing found. No need to call setShaderBinary.
        return;
    }

    // Let the GPU crash dump decoder know about the shader debug data that
    // was found.
    setShaderBinary(sourceDebugInfo.data(), sourceDebugInfo.size());
}

最后,如果不再需要解码器对象,应销毁它以释放为其分配的所有内存

// Destroy the GPU crash dump decoder object.
AFTERMATH_CHECK_ERROR(GFSDK_Aftermath_GpuCrashDump_DestroyDecoder(decoder));

Shader 编译

D3D12

以下是使用 Microsoft DirectX Shader Compiler 为 HLSL shader 生成源 shader 调试信息的受支持变体

  1. 编译并使用完整的 shader blobs

    编译带有调试信息的 shader。在运行应用程序时使用完整的(即,未剥离的)shader 二进制文件,并通过 ShaderLookupCallback 使其可访问。在这种情况下,无需提供 ShaderSourceDebugDataLookupCallback

    编译示例

    dxc -Zi [..] -Fo shader.bin shader.hlsl
    
  2. 编译并剥离

    编译带有调试信息的 shader,然后剥离调试信息。在运行应用程序时使用剥离后的 shader 二进制数据。通过 ShaderLookupCallback 使剥离后的 shader 二进制数据可访问。此外,通过 ShaderSourceDebugDataLookupCallback 使未剥离的 shader 二进制数据可访问。

    编译示例

    dxc -Zi [..] -Fo full_shader.bin shader.hlsl
    dxc -dumpbin -Qstrip_debug -Fo shader.bin full_shader.bin
    

    实现 ShaderSourceDebugDataLookupCallback 所需的 shader 的 DebugName 可以使用 GFSDK_Aftermath_GetShaderDebugName 从剥离或未剥离的 shader 二进制数据中提取。

  3. 编译时使用单独的调试信息(和自动生成的调试数据文件名)

    编译带有调试信息的 shader,并指示编译器将调试元数据存储在单独的 shader 调试信息文件中。编译器生成的文件名将与 shader 的 DebugName 匹配。通过 ShaderLookupCallback 使 shader 二进制数据可访问。此外,通过 ShaderSourceDebugDataLookupCallback 使来自编译器生成的 shader 调试数据文件的数据可访问。

    编译示例

    dxc -Zi [..] -Fo shader.bin -Fd debugInfo\ shader.hlsl
    

    编译器生成的调试数据文件不包含对 shader 的 DebugName 的任何引用。提供 ShaderSourceDebugDataLookupCallback 回调的用户有责任实现一种解决方案,以根据生成的调试数据文件的名称查找调试数据。

  4. 编译时使用单独的调试信息(和用户定义的调试数据文件名)

    编译带有调试信息的 shader,并指示编译器将调试元数据存储在单独的 shader 调试信息文件中。文件名由用户自由选择。通过 ShaderLookupCallback 使 shader 二进制数据可访问。此外,通过 ShaderSourceDebugDataLookupCallback 使来自编译器生成的 shader 调试数据文件的数据可访问。

    编译示例

    dxc -Zi [..] -Fo shader.bin -Fd debugInfo\shader.dbg shader.hlsl
    

    编译器生成的调试数据文件不包含对 shader 的 DebugName 的任何引用。提供 ShaderSourceDebugDataLookupCallback 回调的用户有责任实现一种解决方案,该解决方案基于 shader 的 DebugName 与为编译选择的调试数据文件名的映射来执行调试数据的查找。可以使用 GFSDK_Aftermath_GetShaderDebugName 从 shader 二进制数据中提取 shader 的 DebugName。

Vulkan (SPIR-V)

对于 SPIR-V shader,Aftermath SDK 提供了以下生成源 shader 调试信息的变体支持

  1. 编译并使用完整的 shader blob

    编译带有调试信息的 shader。在运行应用程序时使用完整的(即,未剥离的)shader 二进制文件,并通过 ShaderLookupCallback 使其可访问。在这种情况下,无需提供 ShaderSourceDebugInfoLookupCallback

    使用 Vulkan SDK 工具链的编译示例

    glslangValidator -V -g -o shader.spv shader.vert
    

    使用 DirectX Shader Compiler 的编译示例

    dxc -spirv -Zi [..] -Fo shader.spv shader.hlsl
    
  2. 编译并剥离

    编译带有调试信息的 shader,然后剥离调试信息。在运行应用程序时使用剥离后的 shader 二进制数据。通过 shaderLookupCb 使剥离后的 shader 二进制数据可访问。此外,通过 ShaderSourceDebugInfoLookupCallback 使未剥离的 shader 二进制数据可访问。

    使用 Vulkan SDK 工具链的编译示例

    glslangValidator -V -g -o ./full/shader.spv shader.vert
    spirv-remap --map all --strip-all --input full/shader.spv --output ./stripped/
    

    使用 Microsoft DirectX Shader Compiler 的编译示例

    dxc -spirv -Zi [..] -Fo ./full/shader.spv shader.hlsl
    spirv-remap --map all --strip-all --input full/shader.spv --output ./stripped/
    

    然后,(崩溃转储解码器)应用程序需要将 full/shader.spvstripped/shader.spv 对的内容传递给 GFSDK_Aftermath_GetDebugNameSpirv,以生成与 ShaderSourceDebugInfoLookupCallback 一起使用的 shader DebugName。