From ffff343e3d70be00d2958828c4c94d0f29b3ccb8 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 23 Nov 2017 17:56:20 +0530 Subject: [PATCH] Work on rendering to FBO --- kitty/blit_fragment.glsl | 11 ++++++ kitty/blit_vertex.glsl | 30 +++++++++++++++++ kitty/child-monitor.c | 2 +- kitty/gl.h | 8 +++-- kitty/shaders.c | 73 ++++++++++++++++++++++++++-------------- kitty/state.c | 1 + kitty/state.h | 3 +- kitty/window.py | 3 +- 8 files changed, 101 insertions(+), 30 deletions(-) create mode 100644 kitty/blit_fragment.glsl create mode 100644 kitty/blit_vertex.glsl diff --git a/kitty/blit_fragment.glsl b/kitty/blit_fragment.glsl new file mode 100644 index 000000000..f7b319180 --- /dev/null +++ b/kitty/blit_fragment.glsl @@ -0,0 +1,11 @@ +#version GLSL_VERSION + +uniform sampler2D image; + +in vec2 texcoord; +out vec4 color; + +void main() { + color = texture(image, texcoord); + color = vec4(color.rgb / color.a, color.a); +} diff --git a/kitty/blit_vertex.glsl b/kitty/blit_vertex.glsl new file mode 100644 index 000000000..4f69e8ff8 --- /dev/null +++ b/kitty/blit_vertex.glsl @@ -0,0 +1,30 @@ +#version GLSL_VERSION +#define vleft -1.0 +#define vtop 1.0 +#define vright 1.0 +#define vbottom -1.0 + +#define tleft 0 +#define ttop 1 +#define tright 1 +#define tbottom 0 + +const vec2 viewport_xpos = vec2(vleft, vright); +const vec2 viewport_ypos = vec2(vtop, vbottom); +const vec2 texture_xpos = vec2(tleft, tright); +const vec2 texture_ypos = vec2(ttop, tbottom); + +const uvec2 pos_map[] = uvec2[4]( + uvec2(1, 0), // right, top + uvec2(1, 1), // right, bottom + uvec2(0, 1), // left, bottom + uvec2(0, 0) // left, top +); + +out vec2 texcoord; + +void main() { + uvec2 pos = pos_map[gl_VertexID]; + gl_Position = vec4(viewport_xpos[pos[0]], viewport_ypos[pos[1]], 0, 1); + texcoord = vec2(texture_xpos[pos[0]], texture_ypos[pos[1]]); +} diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 1b0d9245d..03b5c4a12 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -601,7 +601,7 @@ render(double now) { if (!w->num_tabs || !should_os_window_be_rendered(w)) continue; make_os_window_context_current(w); if (w->viewport_size_dirty) { - update_surface_size(w->viewport_width, w->viewport_height); + update_surface_size(w->viewport_width, w->viewport_height, w->offscreen_texture_id); w->viewport_size_dirty = false; } unsigned int active_window_id = 0; diff --git a/kitty/gl.h b/kitty/gl.h index 3cd01a413..b68c4c683 100644 --- a/kitty/gl.h +++ b/kitty/gl.h @@ -63,8 +63,12 @@ gl_init() { } void -update_surface_size(int w, int h) { +update_surface_size(int w, int h, GLuint offscreen_texture_id) { glViewport(0, 0, w, h); + if (offscreen_texture_id) { + glBindTexture(GL_TEXTURE_2D, offscreen_texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + } } void @@ -251,7 +255,7 @@ typedef struct { ssize_t buffers[10]; } VAO; -static VAO vaos[2*MAX_CHILDREN + 10] = {{0}}; +static VAO vaos[4*MAX_CHILDREN + 10] = {{0}}; static ssize_t create_vao() { diff --git a/kitty/shaders.c b/kitty/shaders.c index d45119da0..863d2c230 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -9,8 +9,8 @@ #include "fonts.h" #include -enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, CURSOR_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, NUM_PROGRAMS }; -enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT }; +enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, CURSOR_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, BLIT_PROGRAM, NUM_PROGRAMS }; +enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BLIT_UNIT }; // Sprites {{{ typedef struct { @@ -142,6 +142,8 @@ typedef struct { } CellProgramLayout; static CellProgramLayout cell_program_layouts[NUM_PROGRAMS]; +static GLuint offscreen_framebuffer = 0; +static ssize_t blit_vertex_array; static void init_cell_program() { @@ -158,7 +160,8 @@ init_cell_program() { C(p, colors, 0); C(p, sprite_coords, 1); C(p, is_selected, 2); } #undef C - + glGenFramebuffers(1, &offscreen_framebuffer); + blit_vertex_array = create_vao(); } #define CELL_BUFFERS enum { cell_data_buffer, selection_buffer, uniform_buffer }; @@ -278,7 +281,6 @@ static void draw_graphics(ssize_t vao_idx, ssize_t gvao_idx, ImageRenderData *data, GLuint start, GLuint count) { bind_vertex_array(gvao_idx); bind_program(GRAPHICS_PROGRAM); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); static bool graphics_constants_set = false; if (!graphics_constants_set) { glUniform1i(glGetUniformLocation(program_id(GRAPHICS_PROGRAM), "image"), GRAPHICS_UNIT); @@ -312,34 +314,55 @@ draw_all_cells(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) { } static void -draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) { - (void)vao_idx; (void)gvao_idx; (void)screen; - /* bind_program(CELL_BACKGROUND_PROGRAM); */ - /* glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); */ - /* glEnable(GL_BLEND); */ - /* glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); */ - /* */ - /* if (screen->grman->num_of_negative_refs) draw_graphics(vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_negative_refs); */ - /* */ - /* bind_program(CELL_SPECIAL_PROGRAM); */ - /* glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); */ - /* */ - /* bind_program(CELL_FOREGROUND_PROGRAM); */ - /* glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); */ - /* */ - /* if (screen->grman->num_of_positive_refs) draw_graphics(vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs, screen->grman->num_of_positive_refs); */ - /* glDisable(GL_BLEND); */ +draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *os_window) { + if (!os_window->offscreen_texture_id) { + glGenTextures(1, &os_window->offscreen_texture_id); + glBindTexture(GL_TEXTURE_2D, os_window->offscreen_texture_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 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); + } + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, offscreen_framebuffer); + glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, os_window->offscreen_texture_id, 0); + /* if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) fatal("offscreen framebuffer not complete"); */ + + bind_program(CELL_BG_PROGRAM); + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); + glEnable(GL_BLEND); + if (screen->grman->num_of_negative_refs) draw_graphics(vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_negative_refs); + + bind_program(CELL_SPECIAL_PROGRAM); + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); + + bind_program(CELL_FG_PROGRAM); + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); + + if (screen->grman->num_of_positive_refs) draw_graphics(vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs, screen->grman->num_of_positive_refs); + glDisable(GL_BLEND); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + + // Now render the framebuffer to the screen reversing alpha pre-multiplication + glEnable(GL_SCISSOR_TEST); + bind_program(BLIT_PROGRAM); bind_vertex_array(blit_vertex_array); + 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); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + glDisable(GL_SCISSOR_TEST); } void draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, Screen *screen, OSWindow *os_window) { cell_prepare_to_render(vao_idx, gvao_idx, screen, xstart, ystart, dx, dy); - GLfloat h = (GLfloat)screen->lines * dy; + GLfloat w = (GLfloat)screen->columns * dx, h = (GLfloat)screen->lines * dy; #define SCALE(w, x) ((GLfloat)(os_window->viewport_##w) * (GLfloat)(x)) glScissor( (GLint)(SCALE(width, (xstart + 1.0f) / 2.0f)), (GLint)(SCALE(height, ((ystart - h) + 1.0f) / 2.0f)), - (GLsizei)(ceilf(SCALE(width, (float)screen->columns * dx / 2.0f))), + (GLsizei)(ceilf(SCALE(width, w / 2.0f))), (GLsizei)(ceilf(SCALE(height, h / 2.0f))) ); #undef SCALE @@ -350,7 +373,7 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GL cell_constants_set = true; } bool needs_complex_rendering = screen->grman->num_of_negative_refs || (screen->grman->num_of_positive_refs && os_window->is_semi_transparent); - if (needs_complex_rendering) draw_cells_interleaved(vao_idx, gvao_idx, screen); + if (needs_complex_rendering) draw_cells_interleaved(vao_idx, gvao_idx, screen, os_window); else draw_all_cells(vao_idx, gvao_idx, screen); } // }}} @@ -527,7 +550,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(CURSOR_PROGRAM); C(BORDERS_PROGRAM); C(GRAPHICS_PROGRAM); + C(CELL_PROGRAM); C(CELL_BG_PROGRAM); C(CELL_SPECIAL_PROGRAM); C(CELL_FG_PROGRAM); C(CURSOR_PROGRAM); C(BORDERS_PROGRAM); C(GRAPHICS_PROGRAM); C(BLIT_PROGRAM); C(GLSL_VERSION); C(GL_VERSION); C(GL_VENDOR); diff --git a/kitty/state.c b/kitty/state.c index 6981898f0..b9adf7a4b 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -174,6 +174,7 @@ 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); remove_vao(w->tab_bar_render_data.vao_idx); free(w->tabs); w->tabs = NULL; } diff --git a/kitty/state.h b/kitty/state.h index c0c8694ce..7b7d51266 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -103,6 +103,7 @@ typedef struct { double last_resize_at; bool has_pending_resizes; bool is_semi_transparent; + uint32_t offscreen_texture_id; } OSWindow; @@ -159,7 +160,7 @@ ssize_t create_graphics_vao(); ssize_t create_border_vao(); void draw_cells(ssize_t, ssize_t, float, float, float, float, Screen *, OSWindow *); void draw_cursor(CursorRenderInfo *, bool); -void update_surface_size(int, int); +void update_surface_size(int, int, uint32_t); void free_texture(uint32_t*); void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool); void send_sprite_to_gpu(unsigned int, unsigned int, unsigned int, uint8_t*); diff --git a/kitty/window.py b/kitty/window.py index f3d26b5bb..8f48b3aa3 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -12,7 +12,7 @@ from .constants import ( ScreenGeometry, WindowGeometry, appname, get_boss, wakeup ) from .fast_data_types import ( - BRACKETED_PASTE_END, BRACKETED_PASTE_START, CELL_BG_PROGRAM, + BLIT_PROGRAM, BRACKETED_PASTE_END, BRACKETED_PASTE_START, CELL_BG_PROGRAM, CELL_FG_PROGRAM, CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CURSOR_PROGRAM, GRAPHICS_PROGRAM, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, Screen, add_window, compile_program, glfw_post_empty_event, init_cell_program, @@ -54,6 +54,7 @@ def calculate_gl_geometry(window_geometry, viewport_width, viewport_height, cell def load_shader_programs(semi_transparent=0): v, f = load_shaders('cell') compile_program(GRAPHICS_PROGRAM, *load_shaders('graphics')) + compile_program(BLIT_PROGRAM, *load_shaders('blit')) for which, p in { 'SIMPLE': CELL_PROGRAM, 'BACKGROUND': CELL_BG_PROGRAM,