From 098a38a3a9a3682b30195697bca34050474daa01 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 21 Jun 2023 16:06:07 +0530 Subject: [PATCH] Remove rendering via framebuffer for transparent windows with images There is no need for it with the current rendering pipeline. Images are blended with premult blending. --- kitty/blit_fragment.glsl | 9 --------- kitty/blit_vertex.glsl | 19 ------------------ kitty/cell_fragment.glsl | 8 ++++---- kitty/child-monitor.c | 2 +- kitty/fast_data_types.pyi | 1 - kitty/glfw.c | 2 +- kitty/shaders.c | 41 ++++---------------------------------- kitty/shaders.py | 2 -- kitty/state.c | 2 -- kitty/state.h | 2 -- kitty_tests/check_build.py | 2 +- 11 files changed, 11 insertions(+), 79 deletions(-) delete mode 100644 kitty/blit_fragment.glsl delete mode 100644 kitty/blit_vertex.glsl diff --git a/kitty/blit_fragment.glsl b/kitty/blit_fragment.glsl deleted file mode 100644 index 0d2d0d307..000000000 --- a/kitty/blit_fragment.glsl +++ /dev/null @@ -1,9 +0,0 @@ -uniform sampler2D image; - -in vec2 texcoord; -out vec4 color; - - -void main() { - color = texture(image, texcoord); -} diff --git a/kitty/blit_vertex.glsl b/kitty/blit_vertex.glsl deleted file mode 100644 index c1b8fc4da..000000000 --- a/kitty/blit_vertex.glsl +++ /dev/null @@ -1,19 +0,0 @@ -#define left -1.0f -#define top 1.0f -#define right 1.0f -#define bottom -1.0f - -const vec2 pos_map[] = vec2[4]( - vec2(right, top), - vec2(right, bottom), - vec2(left, bottom), - vec2(left, top) -); - -out vec2 texcoord; - -void main() { - vec2 vertex = pos_map[gl_VertexID]; - gl_Position = vec4(vertex, 0, 1); - texcoord = (vertex + 1.0) / 2.0; -} diff --git a/kitty/cell_fragment.glsl b/kitty/cell_fragment.glsl index 7cda68b11..082de7637 100644 --- a/kitty/cell_fragment.glsl +++ b/kitty/cell_fragment.glsl @@ -64,7 +64,8 @@ vec4 vec4_premul(vec4 rgba) { * multiple passes drawing the background and foreground separately and blending. * * 2a) Opaque bg with images under text - * There are multiple passes, each pass is blended onto the previous using the opaque blend func (alpha, 1- alpha): + * There are multiple passes, each pass is blended onto the previous using the opaque blend func (alpha, 1- alpha). TRANSPARENT is not + * defined in the shaders. * 1) Draw background for all cells * 2) Draw the images that are supposed to be below both the background and text, if any. This happens in the graphics shader * 3) Draw the background of cells that don't have the default background if any images were drawn in 2 above @@ -74,9 +75,8 @@ vec4 vec4_premul(vec4 rgba) { * 7) Draw the images that are supposed to be above text again in the graphics shader * * 2b) Transparent bg with images - * First everything is rendered into a framebuffer, and then the framebuffer is blended onto - * the screen. The framebuffer is needed because it allows access to the background color pixels - * to blend with the image pixels. The steps are basically the same as for 2a. + * Same as (2a) except blending is done with PREMULT_BLEND and TRANSPARENT is defined in the shaders. background_opacity + * is applied to default colored background cells in step (1). * * In this shader exactly *one* of SIMPLE, SPECIAL, FOREGROUND or BACKGROUND will be defined, corresponding * to the appropriate rendering pass from above. diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index e72ed07fc..7a2332390 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -825,7 +825,7 @@ render(monotonic_t now, bool input_read) { bool needs_render = w->is_damaged || w->live_resize.in_progress; if (w->viewport_size_dirty) { w->clear_count = 0; - update_surface_size(w->viewport_width, w->viewport_height, w->offscreen_texture_id); + update_surface_size(w->viewport_width, w->viewport_height, 0); w->viewport_size_dirty = false; needs_render = true; } diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 4b50565b4..8c0d42144 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -259,7 +259,6 @@ NO_CURSOR_SHAPE: int CURSOR_UNDERLINE: int DECAWM: int BGIMAGE_PROGRAM: int -BLIT_PROGRAM: int CELL_BG_PROGRAM: int CELL_FG_PROGRAM: int CELL_PROGRAM: int diff --git a/kitty/glfw.c b/kitty/glfw.c index 4079d1cfa..af7f25a5d 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -326,7 +326,7 @@ framebuffer_size_callback(GLFWwindow *w, int width, int height) { window->live_resize.width = MAX(0, width); window->live_resize.height = MAX(0, height); window->live_resize.num_of_resize_events++; make_os_window_context_current(window); - update_surface_size(width, height, window->offscreen_texture_id); + update_surface_size(width, height, 0); request_tick_callback(); } else log_error("Ignoring resize request for tiny size: %dx%d", width, height); global_state.callback_os_window = NULL; diff --git a/kitty/shaders.c b/kitty/shaders.c index d58cd2ded..d312da9dd 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -16,8 +16,8 @@ #define BLEND_ONTO_OPAQUE_WITH_OPAQUE_OUTPUT glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); // blending onto opaque colors with final color having alpha 1 #define BLEND_PREMULT glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // blending of pre-multiplied colors -enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BLIT_PROGRAM, BGIMAGE_PROGRAM, TINT_PROGRAM, NUM_PROGRAMS }; -enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BLIT_UNIT, BGIMAGE_UNIT }; +enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BGIMAGE_PROGRAM, TINT_PROGRAM, NUM_PROGRAMS }; +enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BGIMAGE_UNIT }; // Sprites {{{ typedef struct { @@ -818,26 +818,12 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen draw_tint(true, screen, crd); glDisable(GL_BLEND); } - if (!os_window->offscreen_texture_id) { - glGenFramebuffers(1, &os_window->offscreen_framebuffer); - glGenTextures(1, &os_window->offscreen_texture_id); - glBindTexture(GL_TEXTURE_2D, os_window->offscreen_texture_id); - glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, os_window->viewport_width, os_window->viewport_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - glBindTexture(GL_TEXTURE_2D, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, os_window->offscreen_framebuffer); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, os_window->offscreen_texture_id, 0); - /* if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) fatal("Offscreen framebuffer not complete"); */ bind_program(CELL_BG_PROGRAM); if (!has_bgimage(os_window)) { // draw background for all cells glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); - } else blank_canvas(0, 0); + } glEnable(GL_BLEND); BLEND_PREMULT; @@ -870,22 +856,7 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); if (!has_bgimage(os_window)) glDisable(GL_BLEND); - glEnable(GL_SCISSOR_TEST); - - // Now render the framebuffer to the screen - bind_program(BLIT_PROGRAM); - static bool blit_constants_set = false; - if (!blit_constants_set) { - glUniform1i(glGetUniformLocation(program_id(BLIT_PROGRAM), "image"), BLIT_UNIT); - blit_constants_set = true; - } - glActiveTexture(GL_TEXTURE0 + BLIT_UNIT); - glBindTexture(GL_TEXTURE_2D, os_window->offscreen_texture_id); - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - glDisable(GL_SCISSOR_TEST); - glDisable(GL_BLEND); } void @@ -964,10 +935,6 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, const ScreenRenderData *srd, OSWin // The scissor limits below are calculated to ensure that they do not // overlap with the pixels outside the draw area. We can't use the actual pixel window dimensions // because of the mapping of opengl's float based co-ord system to pixels. - // for a test case (scissor is used to blit framebuffer in draw_cells_interleaved_premult) run: - // kitty -o background=cyan -o background_opacity=0.7 -o cursor_blink_interval=0 -o window_margin_width=40 -o remember_initial_window_size=n -o initial_window_width=401 kitty +kitten icat --hold logo/kitty.png - // Repeat incrementing window width by 1px each time over cursor_width number of pixels and see if any lines - // appear at the borders of the content area unsigned scissor_base_width = os_window->live_resize.in_progress ? os_window->live_resize.width : (unsigned)os_window->viewport_width; unsigned scissor_base_height = os_window->live_resize.in_progress ? os_window->live_resize.height: (unsigned)os_window->viewport_height; #define SCALE(w, x) ((GLfloat)(scissor_base_##w) * (GLfloat)(x)) @@ -1201,7 +1168,7 @@ static PyMethodDef module_methods[] = { bool init_shaders(PyObject *module) { #define C(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return false; } - C(CELL_PROGRAM); C(CELL_BG_PROGRAM); C(CELL_SPECIAL_PROGRAM); C(CELL_FG_PROGRAM); C(BORDERS_PROGRAM); C(GRAPHICS_PROGRAM); C(GRAPHICS_PREMULT_PROGRAM); C(GRAPHICS_ALPHA_MASK_PROGRAM); C(BLIT_PROGRAM); C(BGIMAGE_PROGRAM); C(TINT_PROGRAM); + C(CELL_PROGRAM); C(CELL_BG_PROGRAM); C(CELL_SPECIAL_PROGRAM); C(CELL_FG_PROGRAM); C(BORDERS_PROGRAM); C(GRAPHICS_PROGRAM); C(GRAPHICS_PREMULT_PROGRAM); C(GRAPHICS_ALPHA_MASK_PROGRAM); C(BGIMAGE_PROGRAM); C(TINT_PROGRAM); C(GLSL_VERSION); C(GL_VERSION); C(GL_VENDOR); diff --git a/kitty/shaders.py b/kitty/shaders.py index 8f8b5c6d0..c22c1974c 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -9,7 +9,6 @@ from typing import Any, Callable, Dict, Iterator, Optional from .constants import read_kitty_resource from .fast_data_types import ( BGIMAGE_PROGRAM, - BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM, @@ -149,7 +148,6 @@ class LoadShaderPrograms: opts = get_options() self.text_old_gamma = opts.text_composition_strategy == 'legacy' self.text_fg_override_threshold = max(0, min(opts.text_fg_override_threshold, 100)) * 0.01 - program_for('blit').compile(BLIT_PROGRAM, allow_recompile) cell = program_for('cell') if self.cell_program_replacer is null_replacer: self.cell_program_replacer = MultiReplacer( diff --git a/kitty/state.c b/kitty/state.c index d06767266..61dfeef15 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -477,8 +477,6 @@ destroy_os_window_item(OSWindow *w) { remove_tab_inner(w, tab->id); } Py_CLEAR(w->window_title); Py_CLEAR(w->tab_bar_render_data.screen); - if (w->offscreen_texture_id) free_texture(&w->offscreen_texture_id); - if (w->offscreen_framebuffer) free_framebuffer(&w->offscreen_framebuffer); remove_vao(w->tab_bar_render_data.vao_idx); remove_vao(w->gvao_idx); free(w->tabs); w->tabs = NULL; diff --git a/kitty/state.h b/kitty/state.h index 843b30742..8924e996b 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -189,7 +189,6 @@ typedef struct { typedef struct { void *handle; id_type id; - uint32_t offscreen_framebuffer; struct { int x, y, w, h; bool is_set, was_maximized; @@ -216,7 +215,6 @@ typedef struct { monotonic_t viewport_resized_at; LiveResizeInfo live_resize; bool has_pending_resizes, is_semi_transparent, shown_once, is_damaged; - uint32_t offscreen_texture_id; unsigned int clear_count; color_type last_titlebar_color; float background_opacity; diff --git a/kitty_tests/check_build.py b/kitty_tests/check_build.py index 543e742f4..025b35656 100644 --- a/kitty_tests/check_build.py +++ b/kitty_tests/check_build.py @@ -32,7 +32,7 @@ class TestBuild(BaseTest): def test_loading_shaders(self) -> None: from kitty.shaders import Program - for name in 'cell border bgimage tint blit graphics'.split(): + for name in 'cell border bgimage tint graphics'.split(): Program(name) def test_glfw_modules(self) -> None: