From f7a4bf6be2f56a0e60946d16844ba99c706eabf6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 08:04:11 +0000 Subject: [PATCH] Reduce per OS Window VRAM usage for windows that use layered rendering, such as translucent windows. The texture used as the intermediate rendering target is now shared between all OS Windows instead of each OS Window having its own. Fixes #9600 Fixes #9599 --- kitty/shaders.c | 32 ++++++++++++++++++++++---------- kitty/state.c | 6 +++++- kitty/state.h | 7 +++++-- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/kitty/shaders.c b/kitty/shaders.c index df7d669bb..1e5d4c8fd 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -1400,18 +1400,28 @@ start_os_window_rendering(OSWindow *os_window, Tab *tab) { // note that during live resize rendering is done in layers if (os_window->live_resize.in_progress) blank_os_window(os_window); if (os_window->needs_layers) { - if (os_window->indirect_output.width != os_window->viewport_width || os_window->indirect_output.height != os_window->viewport_height) { - if (os_window->indirect_output.texture_id) free_texture(&os_window->indirect_output.texture_id); - if (os_window->indirect_output.framebuffer_id) free_framebuffer(&os_window->indirect_output.framebuffer_id); + // Ensure the global shared texture is large enough for this window + if (global_state.layers_render_texture.width < os_window->viewport_width || + global_state.layers_render_texture.height < os_window->viewport_height) { + if (global_state.layers_render_texture.texture_id) free_texture(&global_state.layers_render_texture.texture_id); + if (global_state.layers_render_texture.framebuffer_id) free_framebuffer(&global_state.layers_render_texture.framebuffer_id); + unsigned new_w = (unsigned)MAX(global_state.layers_render_texture.width, os_window->viewport_width); + unsigned new_h = (unsigned)MAX(global_state.layers_render_texture.height, os_window->viewport_height); + setup_texture_as_render_target(new_w, new_h, &global_state.layers_render_texture.texture_id, &global_state.layers_render_texture.framebuffer_id); + global_state.layers_render_texture.width = (int)new_w; + global_state.layers_render_texture.height = (int)new_h; + global_state.layers_render_texture.texture_generation++; } - if (os_window->indirect_output.texture_id == 0) { - os_window->indirect_output.width = os_window->viewport_width; - os_window->indirect_output.height = os_window->viewport_height; - setup_texture_as_render_target((unsigned) os_window->viewport_width, (unsigned)os_window->viewport_height, &os_window->indirect_output.texture_id, &os_window->indirect_output.framebuffer_id); + // Create per-window framebuffer if needed and attach the global texture to it + if (!os_window->indirect_output.framebuffer_id) glGenFramebuffers(1, &os_window->indirect_output.framebuffer_id); + if (os_window->indirect_output.attached_texture_generation != global_state.layers_render_texture.texture_generation) { + bind_framebuffer_for_output(os_window->indirect_output.framebuffer_id); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, global_state.layers_render_texture.texture_id, 0); + os_window->indirect_output.attached_texture_generation = global_state.layers_render_texture.texture_generation; } set_framebuffer_to_use_for_output(os_window->indirect_output.framebuffer_id); bind_framebuffer_for_output(0); - save_viewport_using_bottom_left_origin(0, 0, os_window->indirect_output.width, os_window->indirect_output.height); + save_viewport_using_bottom_left_origin(0, 0, os_window->viewport_width, os_window->viewport_height); clear_current_framebuffer(); draw_bg_image(os_window, tab); } @@ -1425,8 +1435,10 @@ stop_os_window_rendering(OSWindow *os_window, Tab *tab, Window *active_window) { bind_framebuffer_for_output(0); bind_program(BLIT_PROGRAM); glActiveTexture(GL_TEXTURE0 + GRAPHICS_UNIT); - glBindTexture(GL_TEXTURE_2D, os_window->indirect_output.texture_id); - glUniform4f(blit_program_layout.uniforms.src_rect, 0, 1, 1, 0); + glBindTexture(GL_TEXTURE_2D, global_state.layers_render_texture.texture_id); + float sx = global_state.layers_render_texture.width > 0 ? (float)os_window->viewport_width / (float)global_state.layers_render_texture.width : 1.f; + float sy = global_state.layers_render_texture.height > 0 ? (float)os_window->viewport_height / (float)global_state.layers_render_texture.height : 1.f; + glUniform4f(blit_program_layout.uniforms.src_rect, 0, sy, sx, 0); glUniform4f(blit_program_layout.uniforms.dest_rect, -1, 1, 1, -1); restore_viewport(); if (os_window->live_resize.in_progress) save_viewport_using_top_left_origin( diff --git a/kitty/state.c b/kitty/state.c index 56ef919d5..5a5927602 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -491,7 +491,6 @@ destroy_os_window_item(OSWindow *w) { free(w->tabs); w->tabs = NULL; free_bgimage(&w->bgimage, true); zero_at_ptr(&w->bgimage); - if (w->indirect_output.texture_id) free_texture(&w->indirect_output.texture_id); if (w->indirect_output.framebuffer_id) free_framebuffer(&w->indirect_output.framebuffer_id); } @@ -507,6 +506,11 @@ remove_os_window(id_type os_window_id) { REMOVER(global_state.os_windows, os_window_id, global_state.num_os_windows, destroy_os_window_item, global_state.capacity); END_WITH_OS_WINDOW_REFS update_os_window_references(); + if (global_state.num_os_windows == 0) { + if (global_state.layers_render_texture.texture_id) free_texture(&global_state.layers_render_texture.texture_id); + if (global_state.layers_render_texture.framebuffer_id) free_framebuffer(&global_state.layers_render_texture.framebuffer_id); + global_state.layers_render_texture.width = 0; global_state.layers_render_texture.height = 0; + } } return found; } diff --git a/kitty/state.h b/kitty/state.h index b962994ff..0fa8a69d8 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -312,8 +312,7 @@ typedef struct OSWindow { Tab *tabs; BackgroundImage *bgimage; struct { - uint32_t texture_id, framebuffer_id; - int width, height; + uint32_t framebuffer_id, attached_texture_generation; } indirect_output; unsigned int active_tab, num_tabs, capacity, last_active_tab, last_num_tabs, last_active_window_id; bool focused_at_last_render, needs_render, needs_layers; @@ -403,6 +402,10 @@ typedef struct GlobalState { id_type id; bool drag_started; double x, y; } tab_being_dragged; + struct { + uint32_t texture_id, framebuffer_id, texture_generation; + int width, height; + } layers_render_texture; } GlobalState; extern GlobalState global_state;