vkd3d-proton/libs/vkd3d/renderdoc.c

313 lines
9.3 KiB
C

/*
* Copyright 2020 Hans-Kristian Arntzen for Valve Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API
#include "vkd3d_renderdoc.h"
#include "vkd3d_debug.h"
#include "vkd3d_threads.h"
#include "vkd3d_spinlock.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <errno.h>
#include "renderdoc_app.h"
#include "vkd3d_shader.h"
static RENDERDOC_API_1_0_0 *renderdoc_api;
static vkd3d_shader_hash_t renderdoc_capture_shader_hash;
static uint32_t *renderdoc_capture_counts;
static size_t renderdoc_capture_counts_count;
static bool vkd3d_renderdoc_is_active;
static bool vkd3d_renderdoc_global_capture;
static void vkd3d_renderdoc_init_capture_count_list(const char *env)
{
size_t array_size = 0;
uint32_t count;
char *endp;
if (strcmp(env, "-1") == 0)
{
INFO("Doing one big capture of the entire lifetime of a device.\n");
vkd3d_renderdoc_global_capture = true;
return;
}
while (*env != '\0')
{
errno = 0;
count = strtoul(env, &endp, 0);
if (errno != 0)
{
ERR("Error parsing auto counts.\n");
break;
}
vkd3d_array_reserve((void**)&renderdoc_capture_counts, &array_size,
renderdoc_capture_counts_count + 1,
sizeof(*renderdoc_capture_counts));
INFO("Enabling automatic RenderDoc capture of submit #%u.\n", count);
renderdoc_capture_counts[renderdoc_capture_counts_count++] = count;
if (*endp == ',')
env = endp + 1;
else if (*endp != '\0')
{
ERR("Unexpected character %c.\n", *endp);
break;
}
else
env = endp;
}
}
static bool vkd3d_renderdoc_enable_submit_counter(uint32_t counter)
{
size_t i;
/* TODO: Can be smarter about this if we have to. */
for (i = 0; i < renderdoc_capture_counts_count; i++)
if (renderdoc_capture_counts[i] == counter)
return true;
return false;
}
static void vkd3d_renderdoc_init_once(void)
{
char counts[VKD3D_PATH_MAX];
pRENDERDOC_GetAPI get_api;
char env[VKD3D_PATH_MAX];
#ifdef _WIN32
HMODULE renderdoc;
FARPROC fn_ptr;
#else
void *renderdoc;
void *fn_ptr;
#endif
vkd3d_get_env_var("VKD3D_AUTO_CAPTURE_SHADER", env, sizeof(env));
vkd3d_get_env_var("VKD3D_AUTO_CAPTURE_COUNTS", counts, sizeof(counts));
if (strlen(env) == 0 && strlen(counts) == 0)
{
WARN("VKD3D_AUTO_CAPTURE_SHADER or VKD3D_AUTO_CAPTURE_COUNTS is not set, RenderDoc auto capture will not be enabled.\n");
return;
}
if (strlen(counts) == 0)
WARN("VKD3D_AUTO_CAPTURE_COUNTS is not set, will assume that only the first submission is captured.\n");
if (strlen(env) > 0)
renderdoc_capture_shader_hash = strtoull(env, NULL, 16);
if (renderdoc_capture_shader_hash)
INFO("Enabling RenderDoc capture for shader hash: %016"PRIx64".\n", renderdoc_capture_shader_hash);
else
INFO("Enabling RenderDoc capture for all shaders.\n");
if (strlen(counts) > 0)
vkd3d_renderdoc_init_capture_count_list(counts);
else
{
static uint32_t zero_count;
renderdoc_capture_counts = &zero_count;
renderdoc_capture_counts_count = 1;
}
vkd3d_renderdoc_is_active = true;
/* The RenderDoc layer must be loaded. */
#ifdef _WIN32
renderdoc = GetModuleHandleA("renderdoc.dll");
#else
renderdoc = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
#endif
if (!renderdoc)
{
ERR("Failed to load existing RenderDoc library, falling back to using magic vkQueue label.\n");
return;
}
#ifdef _WIN32
fn_ptr = GetProcAddress(renderdoc, "RENDERDOC_GetAPI");
#else
fn_ptr = dlsym(renderdoc, "RENDERDOC_GetAPI");
#endif
/* Workaround compiler warnings about casting to function pointer. */
memcpy(&get_api, &fn_ptr, sizeof(fn_ptr));
if (!get_api)
{
ERR("Failed to load RENDERDOC_GetAPI.\n");
return;
}
if (!get_api(eRENDERDOC_API_Version_1_0_0, (void**)&renderdoc_api))
{
ERR("Failed to obtain RenderDoc API.\n");
renderdoc_api = NULL;
}
}
static pthread_once_t vkd3d_renderdoc_once = PTHREAD_ONCE_INIT;
void vkd3d_renderdoc_init(void)
{
pthread_once(&vkd3d_renderdoc_once, vkd3d_renderdoc_init_once);
}
bool vkd3d_renderdoc_active(void)
{
return vkd3d_renderdoc_is_active;
}
bool vkd3d_renderdoc_global_capture_enabled(void)
{
return vkd3d_renderdoc_global_capture;
}
bool vkd3d_renderdoc_should_capture_shader_hash(vkd3d_shader_hash_t hash)
{
return (renderdoc_capture_shader_hash == hash) || (renderdoc_capture_shader_hash == 0);
}
bool vkd3d_renderdoc_begin_capture(void *instance)
{
static uint32_t overall_counter;
uint32_t counter;
if (!vkd3d_renderdoc_global_capture)
{
counter = vkd3d_atomic_uint32_increment(&overall_counter, vkd3d_memory_order_relaxed) - 1;
if (!vkd3d_renderdoc_enable_submit_counter(counter))
return false;
}
if (renderdoc_api)
renderdoc_api->StartFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), NULL);
return true;
}
bool vkd3d_renderdoc_loaded_api(void)
{
return renderdoc_api != NULL;
}
void vkd3d_renderdoc_end_capture(void *instance)
{
if (renderdoc_api)
renderdoc_api->EndFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), NULL);
}
void vkd3d_renderdoc_command_list_check_capture(struct d3d12_command_list *list,
struct d3d12_pipeline_state *state)
{
unsigned int i;
if (vkd3d_renderdoc_global_capture_enabled())
return;
if (vkd3d_renderdoc_active() && state)
{
if (state->vk_bind_point == VK_PIPELINE_BIND_POINT_COMPUTE)
{
if (vkd3d_renderdoc_should_capture_shader_hash(state->compute.code.meta.hash))
{
WARN("Triggering RenderDoc capture for this command list.\n");
list->debug_capture = true;
}
}
else if (state->vk_bind_point == VK_PIPELINE_BIND_POINT_GRAPHICS)
{
for (i = 0; i < state->graphics.stage_count; i++)
{
if (vkd3d_renderdoc_should_capture_shader_hash(state->graphics.code[i].meta.hash))
{
WARN("Triggering RenderDoc capture for this command list.\n");
list->debug_capture = true;
break;
}
}
}
}
}
bool vkd3d_renderdoc_command_queue_begin_capture(struct d3d12_command_queue *command_queue)
{
const struct vkd3d_vk_device_procs *vk_procs = &command_queue->device->vk_procs;
VkDebugUtilsLabelEXT capture_label;
bool debug_capture;
if (vkd3d_renderdoc_global_capture_enabled())
return false;
debug_capture = vkd3d_renderdoc_begin_capture(command_queue->device->vkd3d_instance->vk_instance);
if (debug_capture && !vkd3d_renderdoc_loaded_api())
{
if (command_queue->device->vk_info.EXT_debug_utils)
{
/* Magic fallback which lets us bridge the Wine barrier over to Linux RenderDoc. */
capture_label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
capture_label.pNext = NULL;
capture_label.pLabelName = "capture-marker,begin_capture";
memset(capture_label.color, 0, sizeof(capture_label.color));
VK_CALL(vkQueueInsertDebugUtilsLabelEXT(command_queue->vkd3d_queue->vk_queue, &capture_label));
}
else
{
ERR("Attempting to use magic tag fallback for RenderDoc captures but VK_EXT_debug_utils is not supported.\n");
debug_capture = false;
}
}
return debug_capture;
}
void vkd3d_renderdoc_command_queue_end_capture(struct d3d12_command_queue *command_queue)
{
const struct vkd3d_vk_device_procs *vk_procs = &command_queue->device->vk_procs;
VkDebugUtilsLabelEXT capture_label;
if (vkd3d_renderdoc_global_capture_enabled())
return;
if (!vkd3d_renderdoc_loaded_api())
{
/* Magic fallback which lets us bridge the Wine barrier over to Linux RenderDoc. */
capture_label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
capture_label.pNext = NULL;
capture_label.pLabelName = "capture-marker,end_capture";
memset(capture_label.color, 0, sizeof(capture_label.color));
VK_CALL(vkQueueInsertDebugUtilsLabelEXT(command_queue->vkd3d_queue->vk_queue, &capture_label));
}
else
vkd3d_renderdoc_end_capture(command_queue->device->vkd3d_instance->vk_instance);
}