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 <robert.mader@collabora.com>
Reviewed-By: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40681>
This commit is contained in:
Robert Mader 2026-03-27 14:56:46 +01:00 committed by Marge Bot
parent c32015da05
commit e68a0dfb12
3 changed files with 70 additions and 0 deletions

View file

@ -1356,6 +1356,14 @@ LLVMpipe driver environment variables
turns off threading completely. The default value is the number of turns off threading completely. The default value is the number of
CPU cores present. 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 VMware SVGA driver environment variables
---------------------------------------- ----------------------------------------

View file

@ -30,6 +30,8 @@
* Keith Whitwell <keithw@vmware.com> * Keith Whitwell <keithw@vmware.com>
*/ */
#include <sys/stat.h>
#include "draw/draw_context.h" #include "draw/draw_context.h"
#include "draw/draw_vbuf.h" #include "draw/draw_vbuf.h"
#include "pipe/p_defines.h" #include "pipe/p_defines.h"
@ -194,6 +196,45 @@ lp_draw_disk_cache_insert_shader(void *cookie,
static enum pipe_reset_status static enum pipe_reset_status
llvmpipe_get_device_reset_status(struct pipe_context *pipe) 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; return PIPE_NO_RESET;
} }
@ -214,6 +255,20 @@ llvmpipe_create_context(struct pipe_screen *screen, void *priv,
memset(llvmpipe, 0, sizeof *llvmpipe); 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->fs_variants_list.list);
list_inithead(&llvmpipe->setup_variants_list.list); list_inithead(&llvmpipe->setup_variants_list.list);

View file

@ -59,6 +59,9 @@ struct lp_velems_state;
struct llvmpipe_context { struct llvmpipe_context {
struct pipe_context pipe; /**< base class */ struct pipe_context pipe; /**< base class */
/** Context creation flags */
unsigned flags;
struct list_head list; struct list_head list;
/** Constant state objects */ /** Constant state objects */
const struct pipe_blend_state *blend; const struct pipe_blend_state *blend;
@ -197,6 +200,10 @@ struct llvmpipe_context {
int max_global_buffers; int max_global_buffers;
struct pipe_resource **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;
}; };