Wayland: Remove the 120ms penalty from waiting for window creation

When showing the window we loop in the wayland backend using a
temporary buffer of blank pixels to force the compositor to finish
setting up the top level surface pronto.

TODO: Set the color of the temmporary buffer to the background color
This commit is contained in:
Kovid Goyal
2024-03-26 15:40:13 +05:30
parent 073f78badb
commit a40a36d191
8 changed files with 65 additions and 32 deletions

View File

@@ -316,7 +316,6 @@ def generate_wrappers(glfw_header: str) -> None:
void glfwWaylandRedrawCSDWindowTitle(GLFWwindow *handle)
void glfwWaylandSetupLayerShellForNextWindow(GLFWLayerShellConfig c)
pid_t glfwWaylandCompositorPID(void)
bool glfwWaylandWindowFullyCreated(GLFWwindow *handle)
unsigned long long glfwDBusUserNotify(const char *app_name, const char* icon, const char *summary, const char *body, \
const char *action_text, int32_t timeout, GLFWDBusnotificationcreatedfun callback, void *data)
void glfwDBusSetUserNotificationHandler(GLFWDBusnotificationactivatedfun handler)

1
glfw/wl_platform.h vendored
View File

@@ -172,6 +172,7 @@ typedef struct _GLFWwindowWayland
struct {
bool surface_configured, fractional_scale_received, preferred_scale_received;
} once;
struct wl_buffer *temp_buffer_used_during_window_creation;
struct {
GLFWLayerShellConfig config;
struct zwlr_layer_surface_v1* zwlr_layer_surface_v1;

69
glfw/wl_window.c vendored
View File

@@ -373,6 +373,10 @@ _glfwWaylandAfterBufferSwap(_GLFWwindow* window) {
// this is not really needed, since I think eglSwapBuffers() calls wl_surface_commit()
// but lets be safe. See https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/egl/drivers/dri2/platform_wayland.c#L1510
wl_surface_commit(window->wl.surface);
if (window->wl.temp_buffer_used_during_window_creation) {
wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);
window->wl.temp_buffer_used_during_window_creation = NULL;
}
}
}
@@ -748,6 +752,51 @@ apply_xdg_configure_changes(_GLFWwindow *window) {
window->wl.pending_state = 0;
}
static bool
attach_temp_buffer_during_window_creation(_GLFWwindow *window) {
int width, height;
_glfwPlatformGetFramebufferSize(window, &width, &height);
const size_t size = 4 * width * height;
int fd = createAnonymousFile(size);
if (fd < 0) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: failed to create anonymouse file");
return false;
}
struct wl_shm_pool *pool = wl_shm_create_pool(_glfw.wl.shm, fd, size);
if (!pool) {
close(fd);
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: failed to create wl_shm_pool of size: %zu", size);
return false;
}
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, width * 4, WL_SHM_FORMAT_ARGB8888);
wl_shm_pool_destroy(pool); close(fd);
if (!buffer) {
_glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: failed to create wl_buffer of size: %zu", size);
return false;
}
if (window->wl.temp_buffer_used_during_window_creation) wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);
window->wl.temp_buffer_used_during_window_creation = buffer;
wl_surface_set_buffer_scale(window->wl.surface, window->wl.fractional_scale ? 1: _glfwWaylandIntegerWindowScale(window));
wl_surface_attach(window->wl.surface, buffer, 0, 0);
if (window->wl.wp_viewport) wp_viewport_set_destination(window->wl.wp_viewport, window->wl.width, window->wl.height);
wl_surface_commit(window->wl.surface);
debug("Attached temp buffer during window creation of size: %dx%d\n", width, height);
return true;
}
static void
loop_till_window_fully_created(_GLFWwindow *window) {
if (!window->wl.window_fully_created) {
monotonic_t start = monotonic();
while (!window->wl.window_fully_created && monotonic() - start < ms_to_monotonic_t(300)) {
if (wl_display_roundtrip(_glfw.wl.display) == -1) {
window->wl.window_fully_created = true;
}
}
window->wl.window_fully_created = true;
}
}
static void
xdgSurfaceHandleConfigure(void* data, struct xdg_surface* surface, uint32_t serial) {
// The poorly documented pattern Wayland requires is:
@@ -763,6 +812,9 @@ xdgSurfaceHandleConfigure(void* data, struct xdg_surface* surface, uint32_t seri
xdg_surface_ack_configure(surface, serial);
debug("XDG surface configure event received and acknowledged\n");
apply_xdg_configure_changes(window);
if (!window->wl.window_fully_created) {
if (!attach_temp_buffer_during_window_creation(window)) window->wl.window_fully_created = true;
}
}
static const struct xdg_surface_listener xdgSurfaceListener = {
@@ -877,6 +929,9 @@ layer_surface_handle_configure(void* data, struct zwlr_layer_surface_v1* surface
layer_set_properties(window);
}
commit_window_surface_if_safe(window);
if (!window->wl.window_fully_created) {
if (!attach_temp_buffer_during_window_creation(window)) window->wl.window_fully_created = true;
}
}
static void
@@ -1200,7 +1255,7 @@ int _glfwPlatformCreateWindow(_GLFWwindow* window,
window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*));
window->wl.monitorsCount = 0;
window->wl.monitorsSize = 1;
if (window->wl.visible) loop_till_window_fully_created(window);
return true;
}
@@ -1220,6 +1275,9 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
_glfw.wl.keyRepeatInfo.keyboardFocusId = 0;
}
if (window->wl.temp_buffer_used_during_window_creation)
wl_buffer_destroy(window->wl.temp_buffer_used_during_window_creation);
if (window->wl.wp_fractional_scale_v1)
wp_fractional_scale_v1_destroy(window->wl.wp_fractional_scale_v1);
if (window->wl.wp_viewport)
@@ -1422,10 +1480,10 @@ void _glfwPlatformMaximizeWindow(_GLFWwindow* window)
void _glfwPlatformShowWindow(_GLFWwindow* window)
{
if (!window->wl.visible)
{
if (!window->wl.visible) {
create_window_desktop_surface(window);
window->wl.visible = true;
loop_till_window_fully_created(window);
}
}
@@ -2585,8 +2643,3 @@ GLFWAPI void glfwWaylandSetupLayerShellForNextWindow(GLFWLayerShellConfig c) {
if (layer_shell_config_for_next_window.output_name && !layer_shell_config_for_next_window.output_name[0]) layer_shell_config_for_next_window.output_name = NULL;
if (layer_shell_config_for_next_window.output_name) layer_shell_config_for_next_window.output_name = strdup(layer_shell_config_for_next_window.output_name);
}
GLFWAPI bool glfwWaylandWindowFullyCreated(GLFWwindow *handle) {
_GLFWwindow* window = (_GLFWwindow*) handle;
return window->wl.window_fully_created;
}

View File

@@ -1574,5 +1574,4 @@ def replace_c0_codes_except_nl_space_tab(text: str) -> str:...
def replace_c0_codes_except_nl_space_tab(text: Union[bytes, memoryview, bytearray]) -> bytes:...
def terminfo_data() -> bytes:...
def wayland_compositor_pid() -> int:...
def window_fully_created(kitty_window_id: int) -> bool: ...
def monotonic() -> float: ...

3
kitty/glfw-wrapper.c generated
View File

@@ -485,9 +485,6 @@ load_glfw(const char* path) {
*(void **) (&glfwWaylandCompositorPID_impl) = dlsym(handle, "glfwWaylandCompositorPID");
if (glfwWaylandCompositorPID_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwWaylandWindowFullyCreated_impl) = dlsym(handle, "glfwWaylandWindowFullyCreated");
if (glfwWaylandWindowFullyCreated_impl == NULL) dlerror(); // clear error indicator
*(void **) (&glfwDBusUserNotify_impl) = dlsym(handle, "glfwDBusUserNotify");
if (glfwDBusUserNotify_impl == NULL) dlerror(); // clear error indicator

4
kitty/glfw-wrapper.h generated
View File

@@ -2316,10 +2316,6 @@ typedef pid_t (*glfwWaylandCompositorPID_func)(void);
GFW_EXTERN glfwWaylandCompositorPID_func glfwWaylandCompositorPID_impl;
#define glfwWaylandCompositorPID glfwWaylandCompositorPID_impl
typedef bool (*glfwWaylandWindowFullyCreated_func)(GLFWwindow*);
GFW_EXTERN glfwWaylandWindowFullyCreated_func glfwWaylandWindowFullyCreated_impl;
#define glfwWaylandWindowFullyCreated glfwWaylandWindowFullyCreated_impl
typedef unsigned long long (*glfwDBusUserNotify_func)(const char*, const char*, const char*, const char*, const char*, int32_t, GLFWDBusnotificationcreatedfun, void*);
GFW_EXTERN glfwDBusUserNotify_func glfwDBusUserNotify_impl;
#define glfwDBusUserNotify glfwDBusUserNotify_impl

View File

@@ -2210,16 +2210,6 @@ make_x11_window_a_dock_window(PyObject *self UNUSED, PyObject *args UNUSED) {
Py_RETURN_NONE;
}
static PyObject*
window_fully_created(PyObject *self UNUSED, PyObject *wid) {
// On Wayland a window does not receive its final size by the time glfwCreateWindow returns
if (!global_state.is_wayland) Py_RETURN_TRUE;
OSWindow *w = os_window_for_kitty_window(PyLong_AsUnsignedLongLong(wid));
if (!w || !w->handle) Py_RETURN_FALSE;
if (glfwWaylandWindowFullyCreated(w->handle)) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
// Boilerplate {{{
static PyMethodDef module_methods[] = {
@@ -2239,7 +2229,6 @@ static PyMethodDef module_methods[] = {
METHODB(glfw_window_hint, METH_VARARGS),
METHODB(x11_display, METH_NOARGS),
METHODB(wayland_compositor_pid, METH_NOARGS),
METHODB(window_fully_created, METH_O),
METHODB(get_click_interval, METH_NOARGS),
METHODB(x11_window_id, METH_O),
METHODB(make_x11_window_a_dock_window, METH_VARARGS),

View File

@@ -86,7 +86,6 @@ from .fast_data_types import (
update_window_title,
update_window_visibility,
wakeup_main_loop,
window_fully_created,
)
from .keys import keyboard_mode_name, mod_mask
from .notify import (
@@ -839,14 +838,14 @@ class Window:
boss = get_boss()
boss.child_monitor.resize_pty(self.id, *current_pty_size)
self.last_resized_at = monotonic()
if not self.child_is_launched and window_fully_created(self.id):
if not self.child_is_launched:
self.child.mark_terminal_ready()
self.child_is_launched = True
update_ime_position = True
if boss.args.debug_rendering:
now = monotonic()
print(f'[{now:.3f}] Child launched {now - self.started_at:.2f} seconds after window creation', file=sys.stderr)
if boss.args.debug_rendering and self.child_is_launched:
print(f'[{now:.3f}] Child launched', file=sys.stderr)
elif boss.args.debug_rendering:
print(f'[{monotonic():.3f}] SIGWINCH sent to child in window: {self.id} with size: {current_pty_size}', file=sys.stderr)
self.last_reported_pty_size = current_pty_size
else: