x11/vulkan: the swapchain presents on an X connection of its own

The X Vulkan context opens a second connection to the server for the
swapchain and hands it to the XCB surface; the window stays the
frontend's. A software WSI writes each frame into its connection as
image data, and on the frontend's connection every request the
frontend makes - the input driver's keymap and pointer queries, the
event pump - waited behind that frame on whichever thread presented.
The connection closes after the swapchain and surface; if it cannot
be opened the frontend's is used. vulkan_surface_create() takes the
XCB connection itself.

The threaded video harness counts the requests on the frontend's
connection over 60 presented frames with the input poll left out and
requires none. The compile matrix builds the context with XCB, as
the real build does.
This commit is contained in:
libretroadmin committed 2026-09-23 15:19:07 +02:00
1 parent 51e356bc3c
commit 04cc6c998d
5 files changed
+122 -13

No files matched your search

+3 -2
View File
@@ -255,8 +255,9 @@ platform_video "ctx: x11 gl" "-DHAVE_X11 -DHAVE_OPENGL -DHAVE_EGL" "" \
gfx/drivers_context/x_ctx.c /usr/include/X11/Xlib.h
platform_video "ctx: x11 egl" "-DHAVE_X11 -DHAVE_EGL -DHAVE_OPENGLES" "" \
gfx/drivers_context/xegl_ctx.c /usr/include/X11/Xlib.h
platform_video "ctx: x11 vulkan" "-DHAVE_VULKAN -DHAVE_X11" "" \
gfx/drivers_context/x_vk_ctx.c /usr/include/vulkan/vulkan.h
platform_video "ctx: x11 vulkan" "-DHAVE_VULKAN -DHAVE_X11 -DHAVE_XCB" "" \
gfx/drivers_context/x_vk_ctx.c \
"/usr/include/vulkan/vulkan.h /usr/include/X11/Xlib-xcb.h"
platform_video "ctx: khr display" "-DHAVE_VULKAN" "" \
gfx/drivers_context/khr_display_ctx.c /usr/include/vulkan/vulkan.h
platform_video "ctx: kms/gbm" "-DHAVE_EGL -DHAVE_OPENGL -DHAVE_KMS -DHAVE_GBM" \
+1 -1
View File
@@ -1007,7 +1007,7 @@ else ifneq (,$(findstring unix,$(platform)))
EXT_TARGET := $(TARGET_NAME)
EXT_INTER_TARGET := $(TARGET_NAME)
INCLUDE += -Ilibretro-common/include -Igfx/include -Ideps -Ideps/rcheevos/include -Ideps/SPIRV-Cross -Ideps/glslang -I.
LIBS += -ldl -lm -lpthread -lGL -ludev -lpulse -lX11 -lX11-xcb -lXxf86vm
LIBS += -ldl -lm -lpthread -lGL -ludev -lpulse -lX11 -lX11-xcb -lxcb -lXxf86vm
endif
ifneq (,$(findstring msvc,$(platform)))
+3 -7
View File
@@ -25,12 +25,6 @@
#include "../../config.h"
#endif
#ifdef HAVE_X11
#ifdef HAVE_XCB
#include <X11/Xlib-xcb.h>
#endif
#endif
#include "vulkan_common.h"
#include "../include/vulkan/vulkan.h"
#include "vksym.h"
@@ -2001,7 +1995,9 @@ bool vulkan_surface_create(gfx_ctx_vulkan_data_t *vk,
surf_info.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR;
surf_info.pNext = NULL;
surf_info.flags = 0;
surf_info.connection = XGetXCBConnection((Display*)display);
/* The caller's connection, which need not be the one
* its window was made on. */
surf_info.connection = (xcb_connection_t*)display;
surf_info.window = *(const xcb_window_t*)surface;
if (create(vk->context.instance,
+42 -3
View File
@@ -27,6 +27,8 @@
#include <compat/strcasestr.h>
#include <retro_timers.h>
#include <X11/Xatom.h>
#include <X11/Xlib-xcb.h>
#include <xcb/xcb.h>
#include "../../configuration.h"
#include "../../frontend/frontend_driver.h"
@@ -50,6 +52,9 @@ typedef struct gfx_ctx_x_vk_data
int interval;
gfx_ctx_vulkan_data_t vk;
/* The swapchain's own connection to the server, see
* gfx_ctx_x_vk_wsi_connection(). */
xcb_connection_t *wsi_conn;
} gfx_ctx_x_vk_data_t;
typedef struct Hints
@@ -85,6 +90,33 @@ static const unsigned long retroarch_icon_vk_data[] = {
static int x_vk_nul_handler(Display *dpy, XErrorEvent *event) { return 0; }
/* The swapchain presents through a connection of its own. A software
* WSI writes each frame into its connection as image data, and every
* request the frontend makes on a shared connection - the input
* driver's keymap and pointer queries, the event pump - would wait
* behind that frame. Nothing the frontend asks the server then queues
* behind a present, whichever thread presents. The window is the
* frontend's; the server does not care which client draws to it. The
* frontend's own connection is used only if a second one cannot be
* opened. */
static xcb_connection_t *gfx_ctx_x_vk_wsi_connection(
gfx_ctx_x_vk_data_t *x)
{
if (!x->wsi_conn)
{
xcb_connection_t *conn = xcb_connect(DisplayString(g_x11_dpy), NULL);
if (xcb_connection_has_error(conn))
{
xcb_disconnect(conn);
RARCH_WARN("[Vulkan] No second X connection for the swapchain,"
" presenting on the frontend's.\n");
return XGetXCBConnection(g_x11_dpy);
}
x->wsi_conn = conn;
}
return x->wsi_conn;
}
static void gfx_ctx_x_vk_destroy_resources(gfx_ctx_x_vk_data_t *x)
{
x11_input_ctx_destroy();
@@ -94,6 +126,14 @@ static void gfx_ctx_x_vk_destroy_resources(gfx_ctx_x_vk_data_t *x)
vulkan_context_destroy(&x->vk, g_x11_win != 0);
}
/* After the swapchain and surface: the server frees what the
* swapchain made on this connection when it closes. */
if (x->wsi_conn)
{
xcb_disconnect(x->wsi_conn);
x->wsi_conn = NULL;
}
if (g_x11_win && g_x11_dpy)
{
#ifdef HAVE_XINERAMA
@@ -467,10 +507,9 @@ static bool gfx_ctx_x_vk_set_video_mode(void *data,
/* FIXME/TODO - threading error here */
/* Use XCB surface since it's the most supported WSI.
* We can obtain the XCB connection directly from X11. */
/* Use XCB surface since it's the most supported WSI. */
if (!vulkan_surface_create(&x->vk, VULKAN_WSI_XCB,
g_x11_dpy, &g_x11_win,
gfx_ctx_x_vk_wsi_connection(x), &g_x11_win,
width, height, x->interval))
goto error;
}
@@ -3973,6 +3973,77 @@ end:
#endif
}
/* ------------------------------------------------------------------ */
/* Lane: the Vulkan swapchain has an X connection of its own */
/* Every request on a connection is numbered, and a present on the */
/* frontend's connection (a software WSI's image data every frame) */
/* moves the frontend's numbering with it. The X input driver's */
/* keymap and pointer queries are the frontend's own requests - the */
/* ones a shared connection makes wait behind a frame - so its poll */
/* is left out while counting, and what remains over a run of */
/* presented frames is only what the window poll sends: nothing. */
/* ------------------------------------------------------------------ */
#define WSILANE_FRAMES 60
static void wsilane_no_poll(void *data) { (void)data; }
static void lane_x11_wsi_connection(void)
{
#if defined(HAVE_X11) && defined(HAVE_VULKAN)
unsigned had = failures;
unsigned long before = 0;
unsigned long sent = 0;
const char *drv = getenv("HARNESS_VIDEO_DRIVER");
input_driver_state_t *input_st;
input_driver_t *polling;
input_driver_t quiet;
if (!drv || strcmp(drv, "vulkan"))
{
fprintf(stderr, "[skip] x11 wsi connection lane (driver %s)\n",
drv ? drv : "null");
return;
}
if ( video_driver_display_type_get() != RARCH_DISPLAY_X11
|| !g_x11_dpy)
{
fprintf(stderr, "[skip] x11 wsi connection lane (not an X11 display)\n");
return;
}
expect_wrapper(false, "x11 wsi connection");
input_st = input_state_get_ptr();
polling = input_st->current_driver;
if (polling)
{
quiet = *polling;
quiet.poll = wsilane_no_poll;
input_st->current_driver = &quiet;
}
run_frames(2);
XSync(g_x11_dpy, False);
before = NextRequest(g_x11_dpy);
run_frames(WSILANE_FRAMES);
XSync(g_x11_dpy, False);
/* Less the XSync that reads the count. */
sent = NextRequest(g_x11_dpy) - before - 1;
if (polling)
input_st->current_driver = polling;
CHECK(sent < WSILANE_FRAMES / 4,
"x11 wsi connection: %lu requests on the frontend's connection"
" over %u presented frames - the swapchain presents on it",
sent, (unsigned)WSILANE_FRAMES);
if (failures == had)
fprintf(stderr, "[pass] x11 wsi connection lane (%lu requests over %u frames)\n",
sent, (unsigned)WSILANE_FRAMES);
#endif
}
int main(int argc, char *argv[])
{
char cfg_path[512];
@@ -4120,6 +4191,8 @@ int main(int argc, char *argv[])
lane_driver_reloads();
if (real_driver())
lane_x11_event_pump();
if (real_driver())
lane_x11_wsi_connection();
if (!real_driver())
{
lane_waiter_call();