From e68a0dfb1261230df81f2044e7c1f5e3e5c00bf3 Mon Sep 17 00:00:00 2001 From: Robert Mader Date: Fri, 27 Mar 2026 14:56:46 +0100 Subject: [PATCH] llvmpipe: Implement manual context resets Make it possible to trigger a emulated GPU reset by creating a file at the path of the `LP_CONTEXT_RESET_FILE` env var. All llvmpipe contexts using the `LOSE_CONTEXT_ON_RESET` strategy that where created before the that file will start returning `PIPE_UNKNOWN_CONTEXT_RESET` in `get_device_reset_status()`, while newer contexts - most notably those created by apps in reaction to the device reset - continue to return `PIPE_NO_RESET`. Note that, because we're dealing with files, rtime is used instead of mtime as the later Usage: ``` LP_CONTEXT_RESET_FILE=~/llvmpipe_reset LIBGL_ALWAYS_SOFTWARE=1 someapp touch ~/llvmpipe_reset ``` Signed-off-by: Robert Mader Reviewed-By: Mike Blumenkrantz Part-of: --- docs/envvars.rst | 8 ++++ src/gallium/drivers/llvmpipe/lp_context.c | 55 +++++++++++++++++++++++ src/gallium/drivers/llvmpipe/lp_context.h | 7 +++ 3 files changed, 70 insertions(+) diff --git a/docs/envvars.rst b/docs/envvars.rst index 2261fd915db..bf9cb9f4074 100644 --- a/docs/envvars.rst +++ b/docs/envvars.rst @@ -1356,6 +1356,14 @@ LLVMpipe driver environment variables turns off threading completely. The default value is the number of CPU cores present. +.. envvar:: LP_CONTEXT_RESET_FILE + + a file path. If set, contexts using the LOSE_CONTEXT_ON_RESET strategy will + check it for the presence and modification time of a file and trigger an + emulated device reset if they were created before the last modification time. + + Currently not available on Windows. + VMware SVGA driver environment variables ---------------------------------------- diff --git a/src/gallium/drivers/llvmpipe/lp_context.c b/src/gallium/drivers/llvmpipe/lp_context.c index cf598434bb1..d13a09cf90b 100644 --- a/src/gallium/drivers/llvmpipe/lp_context.c +++ b/src/gallium/drivers/llvmpipe/lp_context.c @@ -30,6 +30,8 @@ * Keith Whitwell */ +#include + #include "draw/draw_context.h" #include "draw/draw_vbuf.h" #include "pipe/p_defines.h" @@ -194,6 +196,45 @@ lp_draw_disk_cache_insert_shader(void *cookie, static enum pipe_reset_status llvmpipe_get_device_reset_status(struct pipe_context *pipe) { +#if !DETECT_OS_WINDOWS + struct llvmpipe_context *llvmpipe = llvmpipe_context(pipe); + struct stat st_reset_file; + struct timespec ts_now; + int64_t now_ns; + + if (!(llvmpipe->flags & PIPE_CONTEXT_LOSE_CONTEXT_ON_RESET) || + llvmpipe->context_reset_file_path == NULL) + return PIPE_NO_RESET; + + clock_gettime(CLOCK_REALTIME, &ts_now); + now_ns = (int64_t)ts_now.tv_sec * 1000000000LL + ts_now.tv_nsec; + + /* + * Only return a *RESET* status for ~0.5 milliseconds. From the spec: + * + * 5. How should the application react to a reset context event? + * + * RESOLVED: For this extension, the application is expected to query + * the reset status until NO_ERROR is returned. If a reset is encountered, + * at least one *RESET* status will be returned. Once NO_ERROR is again + * encountered, the application can safely destroy the old context and + * create a new one. + */ + if (llvmpipe->context_reset_time_ns > 0) + return now_ns - llvmpipe->context_reset_time_ns < 500000 ? + PIPE_UNKNOWN_CONTEXT_RESET : PIPE_NO_RESET; + + if (stat(llvmpipe->context_reset_file_path, &st_reset_file) == 0) { + int64_t file_mod_time_ns = (int64_t)st_reset_file.st_mtim.tv_sec * + 1000000000LL + st_reset_file.st_mtim.tv_nsec; + + if (llvmpipe->context_creation_time_ns < file_mod_time_ns) { + llvmpipe->context_reset_time_ns = now_ns; + return PIPE_UNKNOWN_CONTEXT_RESET; + } + } + +#endif return PIPE_NO_RESET; } @@ -214,6 +255,20 @@ llvmpipe_create_context(struct pipe_screen *screen, void *priv, memset(llvmpipe, 0, sizeof *llvmpipe); + llvmpipe->flags = flags; +#if !DETECT_OS_WINDOWS + llvmpipe->context_reset_file_path = + os_get_option_secure("LP_CONTEXT_RESET_FILE"); + if (llvmpipe->flags & PIPE_CONTEXT_LOSE_CONTEXT_ON_RESET && + llvmpipe->context_reset_file_path != NULL) { + struct timespec ts_now; + + clock_gettime(CLOCK_REALTIME, &ts_now); + llvmpipe->context_creation_time_ns = + (int64_t)ts_now.tv_sec * 1000000000LL + ts_now.tv_nsec; + } +#endif + list_inithead(&llvmpipe->fs_variants_list.list); list_inithead(&llvmpipe->setup_variants_list.list); diff --git a/src/gallium/drivers/llvmpipe/lp_context.h b/src/gallium/drivers/llvmpipe/lp_context.h index 9716d1e88b6..f53aac75fe2 100644 --- a/src/gallium/drivers/llvmpipe/lp_context.h +++ b/src/gallium/drivers/llvmpipe/lp_context.h @@ -59,6 +59,9 @@ struct lp_velems_state; struct llvmpipe_context { struct pipe_context pipe; /**< base class */ + /** Context creation flags */ + unsigned flags; + struct list_head list; /** Constant state objects */ const struct pipe_blend_state *blend; @@ -197,6 +200,10 @@ struct llvmpipe_context { int max_global_buffers; struct pipe_resource **global_buffers; + /** Used for context reset emulation, see LP_CONTEXT_RESET_FILE */ + const char *context_reset_file_path; + int64_t context_creation_time_ns; + int64_t context_reset_time_ns; };