/**************************************************************************/
/* aftermath_context.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
// Godot headers first so their typedefs don't clash with Windows/SDK macros.
#include "core/error/error_macros.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "core/string/ustring.h"
#include "core/variant/variant.h"
// Platform headers must come BEFORE the SDK headers - the Aftermath SDK gates
// its D3D12/Vulkan PFN typedefs and structs on __d3d12_h__ / VULKAN_H_ being
// already defined. Without this, those typedefs simply don't exist.
#ifdef AFTERMATH_ENABLED
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef AFTERMATH_ENABLED_D3D12
#include <d3d12.h>
#endif
#ifdef AFTERMATH_ENABLED_VULKAN
#include <vulkan/vulkan.h>
#endif
#include "aftermath_headers.h"
#endif // AFTERMATH_ENABLED
#include "aftermath_context.h"
// ---------------------------------------------------------------------------
// Singleton (always compiled)
// ---------------------------------------------------------------------------
AftermathContext &AftermathContext::get() {
static AftermathContext ctx;
return ctx;
}
// ---------------------------------------------------------------------------
// Stub implementations when AFTERMATH_ENABLED is off
// ---------------------------------------------------------------------------
#ifndef AFTERMATH_ENABLED
bool AftermathContext::load_functions() { return false; }
void AftermathContext::register_shader(AftermathShaderType, const uint8_t *, uint32_t) {}
void AftermathContext::initialize_d3d12(void *) {}
void AftermathContext::wait_for_dump() {}
const char *const *AftermathContext::vk_required_extensions() const { return nullptr; }
void *AftermathContext::vk_device_diagnostics_config() { return nullptr; }
#else // AFTERMATH_ENABLED
// ---------------------------------------------------------------------------
// Macro: cast stored void* to the SDK function pointer type and call it.
// Usage: AM_CALL(PFN_GFSDK_Aftermath_Foo, fn_Foo, arg1, arg2, ...)
// ---------------------------------------------------------------------------
#define AM_CALL(PFNType, field, ...) \
(reinterpret_cast<PFNType>(field)(__VA_ARGS__))
// ---------------------------------------------------------------------------
// Environment-variable feature toggles
//
// Defaults are tuned for "post-mortem mode": collect everything needed to
// debug a real crash, but DON'T add new fault sources of our own.
// Set to "0" to disable, anything else (or unset) keeps the default.
// GODOT_AFTERMATH_SHADER_DEBUG_INFO (default: on)
// GODOT_AFTERMATH_RESOURCE_TRACKING (default: on)
// GODOT_AFTERMATH_AUTOMATIC_CHECKPOINTS (default: on)
// GODOT_AFTERMATH_CALLSTACK_CAPTURE (default: on, D3D12 only)
// GODOT_AFTERMATH_MARKERS (default: on, D3D12 only)
// GODOT_AFTERMATH_SHADER_ERROR_REPORTING (default: OFF; turns silent
// shader UB into device-loss)
// ---------------------------------------------------------------------------
struct AftermathEnvOption {
const char *name;
bool default_on;
const char *description;
};
static const AftermathEnvOption AFTERMATH_ENV_OPTIONS[] = {
{ "GODOT_AFTERMATH_SHADER_DEBUG_INFO", true, "Embed shader debug info (.nvdbg) for source-level crash analysis" },
{ "GODOT_AFTERMATH_RESOURCE_TRACKING", true, "Track resource usage for richer crash reports" },
{ "GODOT_AFTERMATH_AUTOMATIC_CHECKPOINTS", true, "Vulkan only: insert automatic checkpoints" },
{ "GODOT_AFTERMATH_CALLSTACK_CAPTURE", true, "D3D12 only: capture CPU callstacks at submit" },
{ "GODOT_AFTERMATH_MARKERS", true, "D3D12 only: enable user event markers" },
{ "GODOT_AFTERMATH_SHADER_ERROR_REPORTING", false, "Strict: turn silent shader UB into device-loss" },
};
static bool _aftermath_env_flag(const char *p_name, bool p_default) {
OS *os = OS::get_singleton();
if (os == nullptr || !os->has_environment(p_name)) {
return p_default;
}
const String value = os->get_environment(p_name);
const bool is_disabled = (value == "0");
return !is_disabled;
}
static void _aftermath_log_env_options() {
print_line("Aftermath: feature toggles (set env var to '0' to disable, anything else enables):");
for (const AftermathEnvOption &opt : AFTERMATH_ENV_OPTIONS) {
const bool active = _aftermath_env_flag(opt.name, opt.default_on);
const char *state = active ? "ON " : "off";
const char *def = opt.default_on ? "default ON " : "default OFF";
print_line(String(" [") + state + "] " + opt.name + " (" + def + ") - " + opt.description);
}
}
// ---------------------------------------------------------------------------
// Static data
// ---------------------------------------------------------------------------
const char *AftermathContext::vk_ext_names[3] = {
#ifdef AFTERMATH_ENABLED_VULKAN
VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME,
#else
nullptr,
nullptr,
#endif
nullptr,
};
// ---------------------------------------------------------------------------
// Loader
// ---------------------------------------------------------------------------
bool AftermathContext::load_functions() {
if (_initialized) {
return true;
}
#ifdef _WIN32
HMODULE lib = LoadLibraryA("GFSDK_Aftermath_Lib.x64.dll");
if (!lib) {
WARN_PRINT("Aftermath: GFSDK_Aftermath_Lib.x64.dll not found - GPU crash dumps disabled. Place the DLL next to the executable.");
return false;
}
#define AM_GETPROC(field, export_name) \
field = (void *)GetProcAddress(lib, export_name); \
if (!field) { print_verbose("Aftermath: missing export " export_name "."); return false; }
AM_GETPROC(fn_EnableGpuCrashDumps, "GFSDK_Aftermath_EnableGpuCrashDumps")
AM_GETPROC(fn_DisableGpuCrashDumps, "GFSDK_Aftermath_DisableGpuCrashDumps")
AM_GETPROC(fn_GetCrashDumpStatus, "GFSDK_Aftermath_GetCrashDumpStatus")
AM_GETPROC(fn_GetShaderDebugInfoIdentifier, "GFSDK_Aftermath_GetShaderDebugInfoIdentifier")
AM_GETPROC(fn_GpuCrashDump_CreateDecoder, "GFSDK_Aftermath_GpuCrashDump_CreateDecoder")
AM_GETPROC(fn_GpuCrashDump_GenerateJSON, "GFSDK_Aftermath_GpuCrashDump_GenerateJSON")
AM_GETPROC(fn_GpuCrashDump_GetJSON, "GFSDK_Aftermath_GpuCrashDump_GetJSON")
AM_GETPROC(fn_GpuCrashDump_DestroyDecoder, "GFSDK_Aftermath_GpuCrashDump_DestroyDecoder")
#ifdef AFTERMATH_ENABLED_D3D12
AM_GETPROC(fn_DX12_Initialize, "GFSDK_Aftermath_DX12_Initialize")
AM_GETPROC(fn_GetShaderHash, "GFSDK_Aftermath_GetShaderHash")
#endif
#ifdef AFTERMATH_ENABLED_VULKAN
AM_GETPROC(fn_GetShaderHashSpirv, "GFSDK_Aftermath_GetShaderHashSpirv")
#endif
#undef AM_GETPROC
_create_dump_dirs();
GFSDK_Aftermath_Result res = AM_CALL(PFN_GFSDK_Aftermath_EnableGpuCrashDumps, fn_EnableGpuCrashDumps,
GFSDK_Aftermath_Version_API,
GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_DX | GFSDK_Aftermath_GpuCrashDumpWatchedApiFlags_Vulkan,
GFSDK_Aftermath_GpuCrashDumpFeatureFlags_DeferDebugInfoCallbacks,
reinterpret_cast<PFN_GFSDK_Aftermath_GpuCrashDumpCb>(&AftermathContext::_cb_gpu_crash_dump),
reinterpret_cast<PFN_GFSDK_Aftermath_ShaderDebugInfoCb>(&AftermathContext::_cb_shader_debug_info),
reinterpret_cast<PFN_GFSDK_Aftermath_GpuCrashDumpDescriptionCb>(&AftermathContext::_cb_description),
reinterpret_cast<PFN_GFSDK_Aftermath_ResolveMarkerCb>(&AftermathContext::_cb_resolve_marker),
this);
if (res != GFSDK_Aftermath_Result_Success) {
WARN_PRINT("Aftermath: EnableGpuCrashDumps failed - GPU crash dumps disabled.");
return false;
}
_initialized = true;
print_line("Aftermath: GPU crash dump capture enabled.");
print_line(String("Aftermath: Output directory -> ") + _dump_dir);
print_line("Aftermath: Crash dumps, shader binaries and debug info are all colocated here. Open the .nv-gpudmp file in Nsight - shaders are auto-discovered.");
_aftermath_log_env_options();
return true;
#else
return false;
#endif // _WIN32
}
// ---------------------------------------------------------------------------
// Dump directory creation
// ---------------------------------------------------------------------------
void AftermathContext::_create_dump_dirs() {
#ifdef _WIN32
char tmp_path[MAX_PATH] = {};
GetTempPathA(MAX_PATH, tmp_path);
// GetTempFileNameA creates a uniquely named file; delete it and create a
// directory with the same name so the path is unique per process.
char tmp_file[MAX_PATH] = {};
GetTempFileNameA(tmp_path, "gdam", 0, tmp_file);
DeleteFileA(tmp_file);
CreateDirectoryA(tmp_file, nullptr);
// Use strncpy_s to avoid MSVC C4996.
strncpy_s(_dump_dir, sizeof(_dump_dir), tmp_file, _TRUNCATE);
#endif
}
// ---------------------------------------------------------------------------
// Shader registration
// ---------------------------------------------------------------------------
void AftermathContext::register_shader(AftermathShaderType p_type, const uint8_t *p_bytes, uint32_t p_size) {
if (!_initialized || !p_bytes || !p_size) {
return;
}
switch (p_type) {
#ifdef AFTERMATH_ENABLED_D3D12
case AFTERMATH_SHADER_DXIL: {
D3D12_SHADER_BYTECODE shader_binary = {};
shader_binary.pShaderBytecode = p_bytes;
shader_binary.BytecodeLength = p_size;
GFSDK_Aftermath_ShaderBinaryHash hash = {};
GFSDK_Aftermath_Result res = AM_CALL(PFN_GFSDK_Aftermath_GetShaderHash, fn_GetShaderHash,
GFSDK_Aftermath_Version_API, &shader_binary, &hash);
if (res != GFSDK_Aftermath_Result_Success) {
return;
}
uint64_t key = hash.hash;
{
MutexLock lock(shader_map_mutex);
if (shader_map.has(key)) {
return;
}
shader_map[key].resize(p_size);
memcpy(shader_map[key].ptrw(), p_bytes, p_size);
}
char path[512] = {};
snprintf(path, sizeof(path), "%s\\%016llx.cso", _dump_dir, (unsigned long long)key);
_write_file(path, p_bytes, p_size);
print_verbose(String("Aftermath: dumped DXIL shader hash=") + String::num_uint64(key, 16, true) + " -> " + path);
} break;
#endif
#ifdef AFTERMATH_ENABLED_VULKAN
case AFTERMATH_SHADER_SPIRV: {
GFSDK_Aftermath_SpirvCode spirv;
spirv.pData = p_bytes;
spirv.size = p_size;
GFSDK_Aftermath_ShaderBinaryHash hash = {};
GFSDK_Aftermath_Result res = AM_CALL(PFN_GFSDK_Aftermath_GetShaderHashSpirv, fn_GetShaderHashSpirv,
GFSDK_Aftermath_Version_API, &spirv, &hash);
if (res != GFSDK_Aftermath_Result_Success) {
return;
}
uint64_t key = hash.hash;
{
MutexLock lock(shader_map_mutex);
if (shader_map.has(key)) {
return;
}
shader_map[key].resize(p_size);
memcpy(shader_map[key].ptrw(), p_bytes, p_size);
}
char path[512] = {};
snprintf(path, sizeof(path), "%s\\%016llx.spv", _dump_dir, (unsigned long long)key);
_write_file(path, p_bytes, p_size);
print_verbose(String("Aftermath: dumped SPIR-V shader hash=") + String::num_uint64(key, 16, true) + " -> " + path);
} break;
#endif
default:
break;
}
}
// ---------------------------------------------------------------------------
// Backend initialization
// ---------------------------------------------------------------------------
void AftermathContext::initialize_d3d12(void *p_d3d12_device) {
#ifdef AFTERMATH_ENABLED_D3D12
if (!_initialized || !p_d3d12_device) {
return;
}
ID3D12Device *device = static_cast<ID3D12Device *>(p_d3d12_device);
uint32_t feature_flags = 0;
if (_aftermath_env_flag("GODOT_AFTERMATH_MARKERS", true)) {
feature_flags |= GFSDK_Aftermath_FeatureFlags_EnableMarkers;
}
if (_aftermath_env_flag("GODOT_AFTERMATH_RESOURCE_TRACKING", true)) {
feature_flags |= GFSDK_Aftermath_FeatureFlags_EnableResourceTracking;
}
if (_aftermath_env_flag("GODOT_AFTERMATH_CALLSTACK_CAPTURE", true)) {
feature_flags |= GFSDK_Aftermath_FeatureFlags_CallStackCapturing;
}
if (_aftermath_env_flag("GODOT_AFTERMATH_SHADER_DEBUG_INFO", true)) {
feature_flags |= GFSDK_Aftermath_FeatureFlags_GenerateShaderDebugInfo;
}
if (_aftermath_env_flag("GODOT_AFTERMATH_SHADER_ERROR_REPORTING", false)) {
feature_flags |= GFSDK_Aftermath_FeatureFlags_EnableShaderErrorReporting;
}
GFSDK_Aftermath_Result res = AM_CALL(PFN_GFSDK_Aftermath_DX12_Initialize, fn_DX12_Initialize,
GFSDK_Aftermath_Version_API,
feature_flags,
device);
if (res != GFSDK_Aftermath_Result_Success) {
WARN_PRINT("Aftermath: DX12_Initialize failed. Shader-level crash data may be missing.");
} else {
WARN_PRINT("Aftermath: D3D12 active. Note: spirv2dxil strips DXIL debug info; only instruction-level crash dumps are available.");
}
#endif
}
// ---------------------------------------------------------------------------
// Vulkan extension helpers
// ---------------------------------------------------------------------------
const char *const *AftermathContext::vk_required_extensions() const {
return vk_ext_names;
}
void *AftermathContext::vk_device_diagnostics_config() {
#ifdef AFTERMATH_ENABLED_VULKAN
static bool built = false;
if (!built) {
built = true;
VkDeviceDiagnosticsConfigCreateInfoNV *info =
reinterpret_cast<VkDeviceDiagnosticsConfigCreateInfoNV *>(vk_diag_config_storage);
memset(info, 0, sizeof(*info));
info->sType = VK_STRUCTURE_TYPE_DEVICE_DIAGNOSTICS_CONFIG_CREATE_INFO_NV;
VkDeviceDiagnosticsConfigFlagsNV flags = 0;
if (_aftermath_env_flag("GODOT_AFTERMATH_SHADER_DEBUG_INFO", true)) {
flags |= VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_DEBUG_INFO_BIT_NV;
}
if (_aftermath_env_flag("GODOT_AFTERMATH_RESOURCE_TRACKING", true)) {
flags |= VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_RESOURCE_TRACKING_BIT_NV;
}
if (_aftermath_env_flag("GODOT_AFTERMATH_AUTOMATIC_CHECKPOINTS", true)) {
flags |= VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_AUTOMATIC_CHECKPOINTS_BIT_NV;
}
if (_aftermath_env_flag("GODOT_AFTERMATH_SHADER_ERROR_REPORTING", false)) {
flags |= VK_DEVICE_DIAGNOSTICS_CONFIG_ENABLE_SHADER_ERROR_REPORTING_BIT_NV;
}
info->flags = flags;
}
print_line("Aftermath: Vulkan GPU crash dump capture active (VK_NV_device_diagnostics_config enabled).");
return vk_diag_config_storage;
#else
return nullptr;
#endif
}
// ---------------------------------------------------------------------------
// Wait for dump flush
// ---------------------------------------------------------------------------
void AftermathContext::wait_for_dump() {
if (!_initialized) {
return;
}
#ifdef _WIN32
constexpr int MAX_WAIT_MS = 5000;
constexpr int SLEEP_MS = 50;
for (int elapsed = 0; elapsed < MAX_WAIT_MS; elapsed += SLEEP_MS) {
GFSDK_Aftermath_CrashDump_Status status = GFSDK_Aftermath_CrashDump_Status_Unknown;
AM_CALL(PFN_GFSDK_Aftermath_GetCrashDumpStatus, fn_GetCrashDumpStatus, &status);
if (status == GFSDK_Aftermath_CrashDump_Status_Finished) {
print_line("Aftermath: crash dump complete.");
return;
}
Sleep(SLEEP_MS);
}
WARN_PRINT("Aftermath: timed out waiting for crash dump.");
#endif
}
// ---------------------------------------------------------------------------
// File helper
// ---------------------------------------------------------------------------
void AftermathContext::_write_file(const char *p_path, const void *p_data, uint32_t p_size) {
#ifdef _WIN32
HANDLE f = CreateFileA(p_path, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (f == INVALID_HANDLE_VALUE) {
return;
}
DWORD written = 0;
WriteFile(f, p_data, p_size, &written, nullptr);
CloseHandle(f);
#endif
}
// ---------------------------------------------------------------------------
// Crash-dump callback: write .nv-gpudmp and JSON sidecar
// ---------------------------------------------------------------------------
void AftermathContext::_cb_gpu_crash_dump(const void *p_data, const uint32_t p_size, void *p_user) {
AftermathContext *self = static_cast<AftermathContext *>(p_user);
char dump_path[512] = {};
#ifdef _WIN32
SYSTEMTIME st = {};
GetLocalTime(&st);
snprintf(dump_path, sizeof(dump_path), "%s\\%04d%02d%02d_%02d%02d%02d.nv-gpudmp",
self->_dump_dir, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
#endif
self->_write_file(dump_path, p_data, p_size);
print_line(String("Aftermath: GPU crash dump written to: ") + dump_path);
// Decode and write JSON sidecar.
GFSDK_Aftermath_GpuCrashDump_Decoder decoder = {};
GFSDK_Aftermath_Result res = AM_CALL(PFN_GFSDK_Aftermath_GpuCrashDump_CreateDecoder,
self->fn_GpuCrashDump_CreateDecoder,
GFSDK_Aftermath_Version_API, p_data, p_size, &decoder);
if (res != GFSDK_Aftermath_Result_Success) {
return;
}
uint32_t json_size = 0;
res = AM_CALL(PFN_GFSDK_Aftermath_GpuCrashDump_GenerateJSON,
self->fn_GpuCrashDump_GenerateJSON,
decoder,
GFSDK_Aftermath_GpuCrashDumpDecoderFlags_ALL_INFO,
GFSDK_Aftermath_GpuCrashDumpFormatterFlags_NONE,
reinterpret_cast<PFN_GFSDK_Aftermath_ShaderDebugInfoLookupCb>(&AftermathContext::_cb_shader_debug_info_lookup),
reinterpret_cast<PFN_GFSDK_Aftermath_ShaderLookupCb>(&AftermathContext::_cb_shader_lookup),
reinterpret_cast<PFN_GFSDK_Aftermath_ShaderSourceDebugInfoLookupCb>(&AftermathContext::_cb_shader_debug_info_lookup),
self,
&json_size);
if (res == GFSDK_Aftermath_Result_Success && json_size > 0) {
Vector<char> json_buf;
json_buf.resize(json_size);
res = AM_CALL(PFN_GFSDK_Aftermath_GpuCrashDump_GetJSON,
self->fn_GpuCrashDump_GetJSON,
decoder, json_size, json_buf.ptrw());
if (res == GFSDK_Aftermath_Result_Success) {
char json_path[512] = {};
snprintf(json_path, sizeof(json_path), "%s.json", dump_path);
self->_write_file(json_path, json_buf.ptr(), json_size);
}
}
AM_CALL(PFN_GFSDK_Aftermath_GpuCrashDump_DestroyDecoder,
self->fn_GpuCrashDump_DestroyDecoder, decoder);
}
// ---------------------------------------------------------------------------
// Shader debug-info callback: write .nvdbg blobs
// ---------------------------------------------------------------------------
void AftermathContext::_cb_shader_debug_info(const void *p_data, const uint32_t p_size, void *p_user) {
AftermathContext *self = static_cast<AftermathContext *>(p_user);
GFSDK_Aftermath_ShaderDebugInfoIdentifier id = {};
GFSDK_Aftermath_Result res = AM_CALL(PFN_GFSDK_Aftermath_GetShaderDebugInfoIdentifier,
self->fn_GetShaderDebugInfoIdentifier,
GFSDK_Aftermath_Version_API,
p_data,
p_size,
&id);
if (res != GFSDK_Aftermath_Result_Success) {
return;
}
char path[512] = {};
snprintf(path, sizeof(path), "%s\\shader-%016llx-%016llx.nvdbg",
self->_dump_dir,
(unsigned long long)id.id[0],
(unsigned long long)id.id[1]);
self->_write_file(path, p_data, p_size);
print_line(String("Aftermath: wrote shader debug info -> ") + path);
}
// ---------------------------------------------------------------------------
// Description callback
// ---------------------------------------------------------------------------
void AftermathContext::_cb_description(void *p_add_value_fn, void *p_user) {
auto add_value = reinterpret_cast<PFN_GFSDK_Aftermath_AddGpuCrashDumpDescription>(p_add_value_fn);
add_value(GFSDK_Aftermath_GpuCrashDumpDescriptionKey_ApplicationName, "Godot Engine");
}
// ---------------------------------------------------------------------------
// Resolve marker callback (no custom markers)
// ---------------------------------------------------------------------------
void AftermathContext::_cb_resolve_marker(const void *, uint32_t, void *, void **pp_resolved, uint32_t *p_resolved_size) {
*pp_resolved = nullptr;
*p_resolved_size = 0;
}
// ---------------------------------------------------------------------------
// Shader lookup callbacks
// ---------------------------------------------------------------------------
void AftermathContext::_cb_shader_lookup(const void *p_hash, void *p_set_shader_fn, void *p_user) {
AftermathContext *self = static_cast<AftermathContext *>(p_user);
const GFSDK_Aftermath_ShaderBinaryHash *hash = static_cast<const GFSDK_Aftermath_ShaderBinaryHash *>(p_hash);
auto set_binary = reinterpret_cast<PFN_GFSDK_Aftermath_SetData>(p_set_shader_fn);
MutexLock lock(self->shader_map_mutex);
uint64_t key = hash->hash;
if (self->shader_map.has(key)) {
const Vector<uint8_t> &bytes = self->shader_map[key];
set_binary(bytes.ptr(), bytes.size());
}
}
void AftermathContext::_cb_shader_debug_info_lookup(const void *, void *, void *) {
// .nvdbg files are written to disk; Nsight Graphics reads them from there.
}
#undef AM_CALL
#endif // AFTERMATH_ENABLED