feat: nescapture capture improvements and drive mounts (#337)

Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kristian Ollikainen
2026-09-18 22:58:22 +03:00
committed by GitHub
parent ebc0242b49
commit 6811c93d51
32 changed files with 3677 additions and 691 deletions

View File

@@ -0,0 +1,93 @@
From 5f615ac88a6f2b962a3e0d68c995f1ab3ec12d20 Mon Sep 17 00:00:00 2001
From: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Date: Thu, 17 Sep 2026 19:33:08 +0300
Subject: [PATCH] ac/linux_drm: cache the device's static VRAM/GTT sizes
AMDGPU_INFO_VRAM_GTT returns total VRAM, CPU-visible VRAM and GTT size.
All three are fixed properties of the device and cannot change while it is
open, but ac_drm_query_heap_info() asked for them on every call.
That is free on a local ioctl. It is not free on an amdgpu native context,
where every query is a synchronous round trip to the host: measured with one
game running under virtio, this single query was 47% of all guest-to-host
traffic, asked roughly 15,000 times a second for an answer that never
changed.
Fetch it once during ac_drm_device_initialize(), before the device is
visible to any other thread, so reading it needs no lock. A failure there is
not fatal -- vram_gtt_valid stays false and the old per-call path is used.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---
src/amd/common/ac_linux_drm.c | 40 +++++++++++++++++++++++++++++------
1 file changed, 34 insertions(+), 6 deletions(-)
diff --git a/src/amd/common/ac_linux_drm.c b/src/amd/common/ac_linux_drm.c
index 21fb2c9e4e3..63b27058ec1 100644
--- a/src/amd/common/ac_linux_drm.c
+++ b/src/amd/common/ac_linux_drm.c
@@ -28,6 +28,23 @@ struct ac_drm_device {
struct util_sync_provider *p;
int fd;
bool is_virtio;
+
+ /* AMDGPU_INFO_VRAM_GTT, fetched once.
+ *
+ * The three sizes it returns -- total VRAM, CPU-visible VRAM, GTT -- are
+ * fixed properties of the device and cannot change while it is open, but
+ * ac_drm_query_heap_info() re-queried them on every call. That is free on a
+ * local ioctl and is not free over virtio, where every query is a
+ * synchronous round trip to the host: measured on an amdgpu native context,
+ * this single query was 47% of all guest-to-host traffic, asked ~15,000
+ * times a second for an answer that never changed.
+ *
+ * Filled during initialize(), before the device is visible to any other
+ * thread, so reading it needs no lock. If the query fails there,
+ * vram_gtt_valid stays false and the old per-call path is used.
+ */
+ struct drm_amdgpu_info_vram_gtt vram_gtt;
+ bool vram_gtt_valid;
};
int ac_drm_device_initialize(int fd, bool is_virtio,
@@ -64,10 +81,17 @@ int ac_drm_device_initialize(int fd, bool is_virtio,
}
}
- if (r == 0)
+ if (r == 0) {
(*dev)->is_virtio = is_virtio;
- else
+ /* Device-static, so it is asked once here rather than on every heap
+ * query. A failure is not fatal: the caller falls back to querying it.
+ */
+ (*dev)->vram_gtt_valid =
+ ac_drm_query_info(*dev, AMDGPU_INFO_VRAM_GTT, sizeof((*dev)->vram_gtt),
+ &(*dev)->vram_gtt) == 0;
+ } else {
free(*dev);
+ }
return r;
}
@@ -757,12 +781,16 @@ int ac_drm_query_gpu_info(ac_drm_device *dev, struct amdgpu_gpu_info *info)
int ac_drm_query_heap_info(ac_drm_device *dev, uint32_t heap, uint32_t flags,
struct amdgpu_heap_info *info)
{
- struct drm_amdgpu_info_vram_gtt vram_gtt_info = {};
+ struct drm_amdgpu_info_vram_gtt vram_gtt_info;
int r;
- r = ac_drm_query_info(dev, AMDGPU_INFO_VRAM_GTT, sizeof(vram_gtt_info), &vram_gtt_info);
- if (r)
- return r;
+ if (dev->vram_gtt_valid) {
+ vram_gtt_info = dev->vram_gtt;
+ } else {
+ r = ac_drm_query_info(dev, AMDGPU_INFO_VRAM_GTT, sizeof(vram_gtt_info), &vram_gtt_info);
+ if (r)
+ return r;
+ }
/* Get heap information */
switch (heap) {

View File

@@ -0,0 +1,80 @@
From ef1f613123fadfd8ef778f77090ba696cb18797e Mon Sep 17 00:00:00 2001
From: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Date: Thu, 17 Sep 2026 19:33:08 +0300
Subject: [PATCH] radv/amdgpu: query all three heaps with one
AMDGPU_INFO_MEMORY
radv_amdgpu_winsys_query_heap_info() wants the usage of VRAM, visible VRAM
and GTT. It called ac_drm_query_heap_info() three times to get them, which
is six ioctls: each of those calls also re-queries the device's static
VRAM_GTT sizes, and then only .heap_usage is used out of the result.
AMDGPU_INFO_MEMORY returns all three heaps together, each with its usage,
which is exactly what this function assembles. One query replaces six.
Six ioctls instead of one is invisible on a local device and is not
invisible over virtio, where each is a synchronous round trip to the host.
Measured on an amdgpu native context with one game running, this function
alone accounted for 94% of all guest-to-host traffic -- around 300 round
trips per frame, against 20 for the actual command submissions -- and with
it most of the frame time.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---
.../vulkan/winsys/amdgpu/radv_amdgpu_winsys.c | 36 +++++++++++--------
1 file changed, 21 insertions(+), 15 deletions(-)
diff --git a/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_winsys.c b/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_winsys.c
index 6aaf8a475c3..a7587f86d85 100644
--- a/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_winsys.c
+++ b/src/amd/vulkan/winsys/amdgpu/radv_amdgpu_winsys.c
@@ -378,7 +378,7 @@ fail:
int
radv_amdgpu_winsys_query_heap_info(ac_drm_device *dev, struct radeon_winsys_heap_info *heap_info)
{
- struct amdgpu_heap_info heap_vram = {0}, heap_vram_vis = {0}, heap_gtt = {0};
+ struct drm_amdgpu_memory_info mem_info = {0};
struct radv_amdgpu_alloc_tracker *alloc_tracker;
int r;
@@ -393,20 +393,26 @@ radv_amdgpu_winsys_query_heap_info(ac_drm_device *dev, struct radeon_winsys_heap
heap_info->allocated_vram_vis = alloc_tracker->allocated_vram_vis;
heap_info->allocated_gtt = alloc_tracker->allocated_gtt;
- /* VRAM usage. */
- r = ac_drm_query_heap_info(dev, AMDGPU_GEM_DOMAIN_VRAM, 0, &heap_vram);
- if (!r)
- heap_info->vram_usage = heap_vram.heap_usage;
-
- /* VRAM visible usage. */
- r = ac_drm_query_heap_info(dev, AMDGPU_GEM_DOMAIN_VRAM, AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED, &heap_vram_vis);
- if (!r)
- heap_info->vram_vis_usage = heap_vram_vis.heap_usage;
-
- /* GTT usage. */
- r = ac_drm_query_heap_info(dev, AMDGPU_GEM_DOMAIN_GTT, 0, &heap_gtt);
- if (!r)
- heap_info->gtt_usage = heap_gtt.heap_usage;
+ /* One query for all three heaps.
+ *
+ * AMDGPU_INFO_MEMORY returns vram, cpu_accessible_vram and gtt together,
+ * each with its usage -- which is the whole of what this function wants.
+ * Three ac_drm_query_heap_info() calls did the same work in six ioctls,
+ * because each of them also re-queried the device's static VRAM_GTT sizes
+ * and then used only .heap_usage out of the result.
+ *
+ * Six ioctls instead of one is invisible on a local device and is not
+ * invisible over virtio, where each is a synchronous round trip to the
+ * host. Measured on an amdgpu native context with one game running, this
+ * function alone accounted for 94% of all guest-to-host traffic and most of
+ * the frame time.
+ */
+ r = ac_drm_query_info(dev, AMDGPU_INFO_MEMORY, sizeof(mem_info), &mem_info);
+ if (!r) {
+ heap_info->vram_usage = mem_info.vram.heap_usage;
+ heap_info->vram_vis_usage = mem_info.cpu_accessible_vram.heap_usage;
+ heap_info->gtt_usage = mem_info.gtt.heap_usage;
+ }
radv_amdgpu_alloc_tracker_release(alloc_tracker);

View File

@@ -0,0 +1,140 @@
From 4dcb0baf29136c90776133afb714bac3c4cb7686 Mon Sep 17 00:00:00 2001
From: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
Date: Thu, 17 Sep 2026 21:50:25 +0300
Subject: [PATCH] radv/amdgpu: cache AMDGPU_INFO_MEMORY briefly on the virtio
path
Heap usage is asked for far more often than it changes. Measured on an
amdgpu native context with one game running, RADV asked for it ~47 times
per 8.8 ms frame -- the same answer, 47 times, each one a synchronous round
trip to the host rather than an ioctl.
Serve a recent answer instead, for one millisecond. That is inside the
contract of what the value is for: heap usage feeds VK_EXT_memory_budget
and the winsys's own eviction decisions, and the spec calls those estimates
that may be out of date. It is also short enough that an application
allocating hard still sees its own pressure within a frame at any plausible
frame rate.
Only on the virtio path, where a query costs a round trip; a local ioctl is
cheap enough that caching it would be complexity for nothing. Only this
query, and only at its natural size: every other AMDGPU_INFO_* either
carries a caller-supplied selector in the union, so one cached answer would
be the wrong answer to the next question, or is asked once at startup.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---
src/amd/common/virtio/amdgpu_virtio.c | 53 +++++++++++++++++++
src/amd/common/virtio/amdgpu_virtio_device.c | 1 +
src/amd/common/virtio/amdgpu_virtio_private.h | 6 +++
3 files changed, 60 insertions(+)
diff --git a/src/amd/common/virtio/amdgpu_virtio.c b/src/amd/common/virtio/amdgpu_virtio.c
index 00494d6a750..dd38ef141f5 100644
--- a/src/amd/common/virtio/amdgpu_virtio.c
+++ b/src/amd/common/virtio/amdgpu_virtio.c
@@ -19,8 +19,25 @@
#include "drm-uapi/amdgpu_drm.h"
#include "util/log.h"
+#include "util/os_time.h"
#include "util/u_math.h"
+/* How long a cached AMDGPU_INFO_MEMORY answer is reused, in nanoseconds.
+ *
+ * Bounded by what the value is for. Heap usage feeds VK_EXT_memory_budget and
+ * the winsys's own eviction decisions, and the spec calls those values
+ * estimates that may be out of date -- so a millisecond of staleness is inside
+ * the contract, while a round trip per ask is not free here the way it is on a
+ * local ioctl.
+ *
+ * A millisecond rather than a frame: it is short enough that an application
+ * allocating hard still sees its own pressure within a frame at any plausible
+ * rate, and long enough to collapse the repeats. Measured on an amdgpu native
+ * context with one game running, RADV asked for this ~47 times per 8.8 ms
+ * frame -- the same answer, 47 synchronous round trips.
+ */
+#define AMDVGPU_MEMORY_INFO_TTL_NS (1000 * 1000)
+
int
amdvgpu_query_info(amdvgpu_device_handle dev, struct drm_amdgpu_info *info)
{
@@ -32,6 +49,30 @@ amdvgpu_query_info(amdvgpu_device_handle dev, struct drm_amdgpu_info *info)
struct amdgpu_ccmd_query_info_rsp *rsp;
assert(0 == (offsetof(struct amdgpu_ccmd_query_info_rsp, payload) % 8));
+ /* AMDGPU_INFO_MEMORY is asked far more often than it changes, and over
+ * virtio every ask is a synchronous round trip to the host rather than an
+ * ioctl. Serve a recent answer instead.
+ *
+ * Only this query, and only at its natural size: every other query either
+ * has a caller-supplied selector in the union -- so one cached answer would
+ * be the wrong answer to the next question -- or is asked once at startup
+ * and costs nothing.
+ */
+ bool cacheable = info->query == AMDGPU_INFO_MEMORY &&
+ info->return_size == sizeof(struct drm_amdgpu_memory_info);
+ if (cacheable) {
+ int64_t now = os_time_get_nano();
+ simple_mtx_lock(&dev->memory_info_mutex);
+ if (dev->memory_info_stamp &&
+ now - dev->memory_info_stamp < AMDVGPU_MEMORY_INFO_TTL_NS) {
+ memcpy((void *)(uintptr_t)info->return_pointer, &dev->memory_info,
+ sizeof(dev->memory_info));
+ simple_mtx_unlock(&dev->memory_info_mutex);
+ return 0;
+ }
+ simple_mtx_unlock(&dev->memory_info_mutex);
+ }
+
req->hdr = AMDGPU_CCMD(QUERY_INFO, req_len);
memcpy(&req->info, info, sizeof(struct drm_amdgpu_info));
@@ -43,6 +84,18 @@ amdvgpu_query_info(amdvgpu_device_handle dev, struct drm_amdgpu_info *info)
memcpy((void*)(uintptr_t)info->return_pointer, rsp->payload, info->return_size);
+ if (cacheable) {
+ simple_mtx_lock(&dev->memory_info_mutex);
+ memcpy(&dev->memory_info, rsp->payload, sizeof(dev->memory_info));
+ /* Stamped after the answer is in hand, so the window covers the time the
+ * value is actually served rather than the round trip that fetched it.
+ * A zero stamp means "never fetched", so a clock that returns zero here
+ * costs a re-fetch rather than pinning a stale answer forever.
+ */
+ dev->memory_info_stamp = os_time_get_nano();
+ simple_mtx_unlock(&dev->memory_info_mutex);
+ }
+
return 0;
}
diff --git a/src/amd/common/virtio/amdgpu_virtio_device.c b/src/amd/common/virtio/amdgpu_virtio_device.c
index eecfd6aa11a..7e58913d520 100644
--- a/src/amd/common/virtio/amdgpu_virtio_device.c
+++ b/src/amd/common/virtio/amdgpu_virtio_device.c
@@ -136,6 +136,7 @@ int amdvgpu_device_initialize(int fd, uint32_t *drm_major, uint32_t *drm_minor,
dev->vdev = vdev;
simple_mtx_init(&dev->handle_to_vbo_mutex, mtx_plain);
+ simple_mtx_init(&dev->memory_info_mutex, mtx_plain);
simple_mtx_init(&dev->contexts_mutex, mtx_plain);
dev->handle_to_vbo = _mesa_hash_table_u64_create(NULL);
diff --git a/src/amd/common/virtio/amdgpu_virtio_private.h b/src/amd/common/virtio/amdgpu_virtio_private.h
index 743f8f74b5c..fb877dc8d28 100644
--- a/src/amd/common/virtio/amdgpu_virtio_private.h
+++ b/src/amd/common/virtio/amdgpu_virtio_private.h
@@ -60,6 +60,12 @@ struct amdvgpu_device {
struct drm_amdgpu_info_device dev_info;
+ /* AMDGPU_INFO_MEMORY, cached for a short while. See amdvgpu_query_info(). */
+ simple_mtx_t memory_info_mutex;
+ struct drm_amdgpu_memory_info memory_info;
+ /* os_time_get_nano() when memory_info was fetched; 0 means never. */
+ int64_t memory_info_stamp;
+
/* Blob id are per drm_file identifiers of host blobs.
* Use a monotically increased integer to assign the blob id.
*/