renderer/opengl: reduce flickering/stretching on resize

Fixes #2446

Two separate issues:

  1. Ensure that our screen size matches the viewport size when
     drawFrame is called. By the time drawFrame is called, GTK will have
     updated the OpenGL context, but our deferred screen size may still
     be incorrect since we wait for the pty to update the screen size.

  2. Do not clear our cells buffer when the screen size changes, instead
     changing to a mechanism that only clears the buffers when we have
     over 50% wasted space.

Co-authored-by: Andrew de los Reyes <adlr@rivosinc.com>
pull/7155/head
Mitchell Hashimoto 2025-04-21 13:55:35 -07:00
parent 3e9b5d491c
commit e5e9d43d52
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
1 changed files with 26 additions and 6 deletions

View File

@ -1779,6 +1779,15 @@ pub fn rebuildCells(
}
}
// Free up memory, generally in case where surface has shrunk.
// If more than half of the capacity is unused, remove all unused capacity.
if (self.cells.items.len * 2 < self.cells.capacity) {
self.cells.shrinkAndFree(self.alloc, self.cells.items.len);
}
if (self.cells_bg.items.len * 2 < self.cells_bg.capacity) {
self.cells_bg.shrinkAndFree(self.alloc, self.cells_bg.items.len);
}
// Some debug mode safety checks
if (std.debug.runtime_safety) {
for (self.cells_bg.items) |cell| assert(cell.mode == .bg);
@ -2196,12 +2205,6 @@ pub fn setScreenSize(
if (single_threaded_draw) self.draw_mutex.lock();
defer if (single_threaded_draw) self.draw_mutex.unlock();
// Reset our buffer sizes so that we free memory when the screen shrinks.
// This could be made more clever by only doing this when the screen
// shrinks but the performance cost really isn't that much.
self.cells.clearAndFree(self.alloc);
self.cells_bg.clearAndFree(self.alloc);
// Store our screen size
self.size = size;
@ -2338,6 +2341,23 @@ pub fn drawFrame(self: *OpenGL, surface: *apprt.Surface) !void {
if (comptime is_darwin) _ = ogl.CGLLockContext(cgl_ctx);
defer _ = if (comptime is_darwin) ogl.CGLUnlockContext(cgl_ctx);
// If our viewport size doesn't match the saved screen size then
// we need to update it. We rely on this over setScreenSize because
// we can pull it directly from the OpenGL context instead of relying
// on the eventual message.
{
var viewport: [4]gl.c.GLint = undefined;
gl.glad.context.GetIntegerv.?(gl.c.GL_VIEWPORT, &viewport);
const screen: renderer.ScreenSize = .{
.width = @intCast(viewport[2]),
.height = @intCast(viewport[3]),
};
if (!screen.equals(self.size.screen)) {
self.size.screen = screen;
self.deferred_screen_size = .{ .size = self.size };
}
}
// Draw our terminal cells
try self.drawCellProgram(gl_state);