Wayland: swap buffers immediately on scale change to ensure attached buffer is a multiple of the new scale

Earlier we were only swapping buffers when ready to draw, but the
Wayland protocol  requires the attached buffer to be a multiple of the
scale. We cannot guarantee an application side swap will be triggered
before the next commit, so instead we blank the new buffer swap it in
the GLFW backend itself.

Fixes #5467
This commit is contained in:
Kovid Goyal 2022-09-10 15:09:46 +05:30
parent 23d73ec933
commit 29b3a92ffc
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 37 additions and 17 deletions

View File

@ -49,6 +49,8 @@ Detailed list of changes
- Wayland: Fix for a bug that could cause kitty to become non-responsive when
using multiple OS windows in a single instance on some compositors (:iss:`5495`)
- Wayland: Fix for a bug preventing kitty from starting on Hyprland when using a non-unit scale (:iss:`5467`)
0.26.2 [2022-09-05]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

44
glfw/wl_window.c vendored
View File

@ -199,7 +199,7 @@ static bool checkScaleChange(_GLFWwindow* window)
}
// Makes the surface considered as XRGB instead of ARGB.
static void setOpaqueRegion(_GLFWwindow* window)
static void setOpaqueRegion(_GLFWwindow* window, bool commit_surface)
{
struct wl_region* region;
@ -209,10 +209,22 @@ static void setOpaqueRegion(_GLFWwindow* window)
wl_region_add(region, 0, 0, window->wl.width, window->wl.height);
wl_surface_set_opaque_region(window->wl.surface, region);
wl_surface_commit(window->wl.surface);
if (commit_surface) wl_surface_commit(window->wl.surface);
wl_region_destroy(region);
}
static void
swap_buffers(_GLFWwindow *window) {
// this will attach the buffer to the surface,
// the client is responsible for clearing the buffer to an appropriate blank
window->swaps_disallowed = false;
GLFWwindow *current = glfwGetCurrentContext();
bool context_is_current = ((_GLFWwindow*)current)->id == window->id;
if (!context_is_current) glfwMakeContextCurrent((GLFWwindow*)window);
window->context.swapBuffers(window);
if (!context_is_current) glfwMakeContextCurrent(current);
}
static void
resizeFramebuffer(_GLFWwindow* window) {
@ -221,7 +233,7 @@ resizeFramebuffer(_GLFWwindow* window) {
int scaledHeight = window->wl.height * scale;
debug("Resizing framebuffer to: %dx%d at scale: %d\n", window->wl.width, window->wl.height, scale);
wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0);
if (!window->wl.transparent) setOpaqueRegion(window);
if (!window->wl.transparent) setOpaqueRegion(window, false);
_glfwInputFramebufferSize(window, scaledWidth, scaledHeight);
}
@ -236,9 +248,9 @@ clipboard_mime(void) {
}
static bool
dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height) {
dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height, bool *scale_changed) {
bool size_changed = width != window->wl.width || height != window->wl.height;
bool scale_changed = checkScaleChange(window);
*scale_changed = checkScaleChange(window);
if (size_changed) {
_glfwInputWindowSize(window, width, height);
@ -246,7 +258,7 @@ dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height
resizeFramebuffer(window);
}
if (scale_changed) {
if (*scale_changed) {
debug("Scale changed to %d in dispatchChangesAfterConfigure\n", window->wl.scale);
if (!size_changed) resizeFramebuffer(window);
_glfwInputWindowContentScale(window, window->wl.scale, window->wl.scale);
@ -254,7 +266,7 @@ dispatchChangesAfterConfigure(_GLFWwindow *window, int32_t width, int32_t height
_glfwInputWindowDamage(window);
return size_changed || scale_changed;
return size_changed || *scale_changed;
}
static void
@ -383,7 +395,7 @@ static bool createSurface(_GLFWwindow* window,
window->wl.scale = 1;
if (!window->wl.transparent)
setOpaqueRegion(window);
setOpaqueRegion(window, false);
return true;
}
@ -498,14 +510,7 @@ static void xdgSurfaceHandleConfigure(void* data,
int height = window->wl.pending.height;
if (!window->wl.surface_configured_once) {
window->wl.surface_configured_once = true;
// this will attach the buffer to the surface, the client is responsible for clearing the buffer to an appropriate blank
window->swaps_disallowed = false;
GLFWwindow *current = glfwGetCurrentContext();
bool context_is_current = ((_GLFWwindow*)current)->id == window->id;
if (!context_is_current) glfwMakeContextCurrent(data);
window->context.swapBuffers(window);
if (!context_is_current) glfwMakeContextCurrent(current);
swap_buffers(window);
if (!width && !height && !new_states && !window->wl.decorations.serverSide && getenv("XAUTHORITY") && strstr(getenv("XAUTHORITY"), "mutter")) {
// https://github.com/kovidgoyal/kitty/issues/4802
debug("Ignoring first empty surface configure event on mutter.\n");
@ -534,10 +539,11 @@ static void xdgSurfaceHandleConfigure(void* data,
}
bool resized = false;
bool scale_changed = false;
if (window->wl.pending_state) {
int width = window->wl.pending.width, height = window->wl.pending.height;
set_csd_window_geometry(window, &width, &height);
resized = dispatchChangesAfterConfigure(window, width, height);
resized = dispatchChangesAfterConfigure(window, width, height, &scale_changed);
if (window->wl.decorations.serverSide) {
free_csd_surfaces(window);
} else {
@ -548,6 +554,10 @@ static void xdgSurfaceHandleConfigure(void* data,
inform_compositor_of_window_geometry(window, "configure");
// we need to swap buffers here to ensure the buffer attached to the surface is a multiple
// of the new scale. See https://github.com/kovidgoyal/kitty/issues/5467
if (scale_changed) swap_buffers(window);
// if a resize happened there will be a commit at the next render frame so
// dont commit here, GNOME doesnt like it and its not really needed anyway
if (!resized) wl_surface_commit(window->wl.surface);

View File

@ -312,6 +312,14 @@ dpi_change_callback(GLFWwindow *w, float x_scale UNUSED, float y_scale UNUSED) {
window->live_resize.last_resize_event_at = monotonic();
global_state.callback_os_window = NULL;
request_tick_callback();
if (global_state.is_wayland) {
// because of the stupidity of Wayland design, the GLFW wayland backend
// will need to swap buffers immediately after a scale change to ensure
// the buffer size is a multiple of the scale. So blank the new buffer to
// ensure we dont leak any unintialized pixels to the screen. The OpenGL viewport
// will already have been resized to its new size in framebuffer_size_callback
blank_os_window(window);
}
}
static void