Wayland: SDL3 改变窗口大小时极其卡顿
SDL3 创建的窗口在改变大小时极其卡顿,测量 while (SDL_PollEvent(&event)) {} 耗时大多
在几百至上千毫秒,严重拖慢了窗口重绘
Gnome 50.1
SDL_GPU Driver: Vulkan
在基于 Gnome 的桌面上存在此问题
问题在 2025 年就已经出现
相关 Issue: Wayland: extreme lag when resizing window
What desktop are you using? This is a known issue with GNOME based desktops, which use the libdecor backend. The root issue is libdecor not passing the resize flag through, which needs a new version of libdecor to be released to be remedied.
原因应为 SDL 使用 libdecor 在 Wayland 下绘制窗口装饰,但是 libdecor 没能向下传递 resize 标志
切换到 XWayland
通过设置 export SDL_VIDEODRIVER=x11 变量可以回退到 X11 后端,临时解决卡顿的问题
自绘标题栏
SDL_SetHint(SDL_HINT_VIDEO_WAYLAND_ALLOW_LIBDECOR, "0");
在 SDL_Init() 前关闭默认的窗口装饰并设置 window flag 为 SDL_WINDOW_BORDERLESS,在 Gnome 下可以用 Super + 鼠标左键 移动,
Super + 鼠标右键 调整窗口大小/关闭/最大最小化等
SDL_SetWindowHitTest
SDL 提供了用于控制窗口属性的 API, 只需要创建一个回调函数并进行坐标判断
typedef enum SDL_HitTestResult
{
SDL_HITTEST_NORMAL, /**< Region is normal. No special properties. */
SDL_HITTEST_DRAGGABLE, /**< Region can drag entire window. */
SDL_HITTEST_RESIZE_TOPLEFT, /**< Region is the resizable top-left corner border. */
SDL_HITTEST_RESIZE_TOP, /**< Region is the resizable top border. */
SDL_HITTEST_RESIZE_TOPRIGHT, /**< Region is the resizable top-right corner border. */
SDL_HITTEST_RESIZE_RIGHT, /**< Region is the resizable right border. */
SDL_HITTEST_RESIZE_BOTTOMRIGHT, /**< Region is the resizable bottom-right corner border. */
SDL_HITTEST_RESIZE_BOTTOM, /**< Region is the resizable bottom border. */
SDL_HITTEST_RESIZE_BOTTOMLEFT, /**< Region is the resizable bottom-left corner border. */
SDL_HITTEST_RESIZE_LEFT /**< Region is the resizable left border. */
} SDL_HitTestResult;
SDL_HitTestResult win_hint_cb(SDL_Window *win, const SDL_Point *area, void *data)
{
int width, height;
SDL_GetWindowSize(win, &width, &height);
if (area->y < 40) {
if (area->y < 5) {
if (area->x < 10) return SDL_HITTEST_RESIZE_TOPLEFT;
else if (width - area->x < 10) return SDL_HITTEST_RESIZE_TOPRIGHT;
else return SDL_HITTEST_RESIZE_TOP;
}
return SDL_HITTEST_DRAGGABLE;
}
if (area->x < 10) {
if (height - area->y < 10) return SDL_HITTEST_RESIZE_BOTTOMLEFT;
else return SDL_HITTEST_RESIZE_LEFT;
}
if (width - area->x < 10) {
if (height - area->y < 10) return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
else return SDL_HITTEST_RESIZE_RIGHT;
}
if (height - area->y < 10) return SDL_HITTEST_RESIZE_BOTTOM;
return SDL_HITTEST_NORMAL;
}
...
SDL_SetWindowHitTest(window, win_hint_cb, NULL);
...
这里设置标题栏高度为 40,窗口边缘触发调整大小的区域为 10 (顶部处为 5)
因为关闭了窗口装饰,所以后续还需要自绘标题栏