diff --git a/configure b/configure
index 8c843fef72..9d0a540fb7 100755
--- a/configure
+++ b/configure
@@ -344,6 +344,7 @@ pvrdma="$default_feature"
 gprof="no"
 debug_tcg="no"
 debug="no"
+renderdoc="$auto"
 sanitizers="no"
 tsan="no"
 fortify_source="$default_feature"
@@ -1112,6 +1113,10 @@ for opt do
   ;;
   --enable-nvmm) nvmm="enabled"
   ;;
+  --disable-renderdoc) renderdoc="disabled"
+  ;;
+  --enable-renderdoc) renderdoc="enabled"
+  ;;
   --disable-whpx) whpx="disabled"
   ;;
   --enable-whpx) whpx="enabled"
@@ -1974,6 +1979,7 @@ disabled with --disable-FEATURE, default is enabled if available
   multiprocess    Out of process device emulation support
   gio             libgio support
   slirp-smbd      use smbd (at path --smbd=*) in slirp networking
+  renderdoc       improved RenderDoc frame capture support
 
 NOTE: The object files are built at the place where configure is launched
 EOF
@@ -5315,7 +5321,8 @@ if test "$skip_meson" = no; then
         -Dattr=$attr -Ddefault_devices=$default_devices -Dvirglrenderer=$virglrenderer \
         -Ddocs=$docs -Dsphinx_build=$sphinx_build -Dinstall_blobs=$blobs \
         -Dvhost_user_blk_server=$vhost_user_blk_server -Dmultiprocess=$multiprocess \
-        -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi -Dbpf=$bpf\
+        -Dfuse=$fuse -Dfuse_lseek=$fuse_lseek -Dguest_agent_msi=$guest_agent_msi -Dbpf=$bpf \
+        -Drenderdoc=$renderdoc \
         $(if test "$default_feature" = no; then echo "-Dauto_features=disabled"; fi) \
 	-Dtcg_interpreter=$tcg_interpreter \
         $cross_arg \
diff --git a/hw/xbox/nv2a/debug.c b/hw/xbox/nv2a/debug.c
index b570cf3168..0e6e84f32e 100644
--- a/hw/xbox/nv2a/debug.c
+++ b/hw/xbox/nv2a/debug.c
@@ -27,7 +27,7 @@
 #include <stdarg.h>
 #include <assert.h>
 
-#ifdef ENABLE_RENDERDOC
+#ifdef CONFIG_RENDERDOC
 #include <renderdoc_app.h>
 #include <dlfcn.h>
 
@@ -61,7 +61,7 @@ void gl_debug_initialize(void)
 #endif
     }
 
-#ifdef ENABLE_RENDERDOC
+#ifdef CONFIG_RENDERDOC
     void* renderdoc = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
     if (renderdoc) {
         pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(
@@ -147,7 +147,7 @@ void gl_debug_label(GLenum target, GLuint name, const char *fmt, ...)
 
 void gl_debug_frame_terminator(void)
 {
-#ifdef ENABLE_RENDERDOC
+#ifdef CONFIG_RENDERDOC
     if (rdoc_api) {
         if (rdoc_api->IsTargetControlConnected()) {
             if (rdoc_api->IsFrameCapturing()) {
@@ -167,15 +167,15 @@ void gl_debug_frame_terminator(void)
     glFrameTerminatorGREMEDY();
 }
 
-#ifdef ENABLE_RENDERDOC
-
+#ifdef CONFIG_RENDERDOC
 bool nv2a_dbg_renderdoc_available(void) {
-  return rdoc_api != NULL;
+    return rdoc_api != NULL;
 }
 
 void nv2a_dbg_renderdoc_capture_frames(uint32_t num_frames) {
-  renderdoc_capture_frames = num_frames;
+    renderdoc_capture_frames = num_frames;
 }
-#endif // ENABLE_RENDERDOC
-
 #endif
+
+#endif // DEBUG_NV2A_GL
+
diff --git a/hw/xbox/nv2a/debug.h b/hw/xbox/nv2a/debug.h
index 7eb574c017..0c2c3d5f76 100644
--- a/hw/xbox/nv2a/debug.h
+++ b/hw/xbox/nv2a/debug.h
@@ -39,11 +39,9 @@
 // #define DEBUG_NV2A_GL
 #ifdef DEBUG_NV2A_GL
 
-// Improve frame capture boundaries with RenderDoc.
-// #define ENABLE_RENDERDOC
-
 #include <stdbool.h>
 #include "gl/gloffscreen.h"
+#include "config-host.h"
 
 void gl_debug_initialize(void);
 void gl_debug_message(bool cc, const char *fmt, ...);
@@ -63,6 +61,19 @@ void gl_debug_frame_terminator(void);
 #define NV2A_GL_DFRAME_TERMINATOR() \
     gl_debug_frame_terminator()
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef CONFIG_RENDERDOC
+bool nv2a_dbg_renderdoc_available(void);
+void nv2a_dbg_renderdoc_capture_frames(uint32_t num_frames);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
 #else
 # define NV2A_GL_DPRINTF(cc, format, ...)          do { \
         if (cc) NV2A_DPRINTF(format "\n", ##__VA_ARGS__ ); \
@@ -151,11 +162,6 @@ extern NV2AStats g_nv2a_stats;
 const char *nv2a_profile_get_counter_name(unsigned int cnt);
 int nv2a_profile_get_counter_value(unsigned int cnt);
 
-#ifdef ENABLE_RENDERDOC
-bool nv2a_dbg_renderdoc_available(void);
-void nv2a_dbg_renderdoc_capture_frames(uint32_t num_frames);
-#endif
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/meson.build b/meson.build
index 59dd377edc..bd92d40179 100644
--- a/meson.build
+++ b/meson.build
@@ -469,7 +469,7 @@ if 'CONFIG_SPICE_PROTOCOL' in config_host
 endif
 rt = cc.find_library('rt', required: false)
 libdl = not_found
-if 'CONFIG_PLUGIN' in config_host
+if 'CONFIG_PLUGIN' in config_host or get_option('renderdoc').enabled()
   libdl = cc.find_library('dl', required: false)
   if not cc.has_function('dlopen', dependencies: libdl)
     error('dlopen not found')
@@ -1224,6 +1224,19 @@ endif
 have_host_block_device = (targetos != 'darwin' or
     cc.has_header('IOKit/storage/IOMedia.h'))
 
+have_renderdoc = false
+if have_system and get_option('renderdoc').enabled()
+  if not cc.has_header('renderdoc_app.h')
+    error('renderdoc required, but renderdoc_app.h was not found')
+  else
+    if targetos == 'linux' and not libdl.found()
+      error('renderdoc required, but libdl was not found')
+    else
+      have_renderdoc = true
+    endif
+  endif
+endif
+
 #################
 # config-host.h #
 #################
@@ -1324,6 +1337,7 @@ config_host_data.set('QEMU_VERSION_MINOR', meson.project_version().split('.')[1]
 config_host_data.set('QEMU_VERSION_MICRO', meson.project_version().split('.')[2])
 
 config_host_data.set('HAVE_HOST_BLOCK_DEVICE', have_host_block_device)
+config_host_data.set('CONFIG_RENDERDOC', have_renderdoc)
 
 # has_header
 config_host_data.set('CONFIG_EPOLL', cc.has_header('sys/epoll.h'))
diff --git a/meson_options.txt b/meson_options.txt
index a9a9b8f4c6..685baef380 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -49,6 +49,8 @@ option('cfi_debug', type: 'boolean', value: 'false',
        description: 'Verbose errors in case of CFI violation')
 option('multiprocess', type: 'feature', value: 'auto',
        description: 'Out of process device emulation support')
+option('renderdoc', type: 'feature', value: 'auto',
+       description: 'RenderDoc frame capture support')
 
 option('attr', type : 'feature', value : 'auto',
        description: 'attr/xattr support')
diff --git a/ui/meson.build b/ui/meson.build
index abd516bcea..256e75583e 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -62,6 +62,9 @@ xemu_ss.add(sdl, opengl, openssl, imgui)
 xemu_ss.add(when: 'CONFIG_LINUX', if_true: [xemu_gtk, files('xemu-os-utils-linux.c', 'noc_file_dialog_gtk.c')])
 xemu_ss.add(when: 'CONFIG_WIN32', if_true: files('xemu-os-utils-windows.c', 'noc_file_dialog_win32.c'))
 xemu_ss.add(when: 'CONFIG_DARWIN', if_true: files('xemu-os-utils-macos.m', 'noc_file_dialog_macos.m'))
+
+xemu_ss.add(whend: 'CONFIG_RENDERDOC', if_true: [libdl])
+
 softmmu_ss.add_all(xemu_ss)
 
 softmmu_ss.add([spice_headers, files('spice-module.c')])
diff --git a/ui/xemu-hud.cc b/ui/xemu-hud.cc
index 6054a5a5ec..99e14f82db 100644
--- a/ui/xemu-hud.cc
+++ b/ui/xemu-hud.cc
@@ -1898,7 +1898,7 @@ static AutoUpdateWindow update_window;
 #endif
 static std::deque<const char *> g_errors;
 
-#ifdef ENABLE_RENDERDOC
+#ifdef CONFIG_RENDERDOC
 static bool capture_renderdoc_frame = false;
 #endif
 
@@ -2063,7 +2063,7 @@ static void process_keyboard_shortcuts(void)
         monitor_window.toggle_open();
     }
 
-#ifdef ENABLE_RENDERDOC
+#if defined(DEBUG_NV2A_GL) && defined(CONFIG_RENDERDOC)
     if (is_key_pressed(SDL_SCANCODE_F10)) {
         nv2a_dbg_renderdoc_capture_frames(1);
     }
@@ -2146,7 +2146,7 @@ static void ShowMainMenu()
             ImGui::MenuItem("Monitor", "~", &monitor_window.is_open);
             ImGui::MenuItem("Audio", NULL, &apu_window.is_open);
             ImGui::MenuItem("Video", NULL, &video_window.is_open);
-#ifdef ENABLE_RENDERDOC
+#if defined(DEBUG_NV2A_GL) && defined(CONFIG_RENDERDOC)
             if (nv2a_dbg_renderdoc_available()) {
                 ImGui::MenuItem("RenderDoc: Capture", NULL, &capture_renderdoc_frame);
             }
@@ -2428,7 +2428,7 @@ void xemu_hud_render(void)
     ImGui::NewFrame();
     process_keyboard_shortcuts();
 
-#ifdef ENABLE_RENDERDOC
+#if defined(DEBUG_NV2A_GL) && defined(CONFIG_RENDERDOC)
     if (capture_renderdoc_frame) {
         nv2a_dbg_renderdoc_capture_frames(1);
         capture_renderdoc_frame = false;