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.
This commit is contained in:
Kovid Goyal 2023-06-21 16:06:07 +05:30
parent 2a96b5cb25
commit 098a38a3a9
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
11 changed files with 11 additions and 79 deletions

View file

@ -1,9 +0,0 @@
uniform sampler2D image;
in vec2 texcoord;
out vec4 color;
void main() {
color = texture(image, texcoord);
}

View file

@ -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;
}

View file

@ -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.

View file

@ -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;
}

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -9,7 +9,6 @@
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 @@ def __call__(self, semi_transparent: bool = False, allow_recompile: bool = False
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(

View file

@ -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;

View file

@ -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;

View file

@ -32,7 +32,7 @@ def test_loading_extensions(self) -> None:
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: