diff --git a/kitty/graphics.c b/kitty/graphics.c index e6d5c154e..5a14d089e 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -68,7 +68,7 @@ next_id(id_type *counter) { static const unsigned PARENT_DEPTH_LIMIT = 8; GraphicsManager* -grman_alloc(void) { +grman_alloc(bool for_paused_rendering) { GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0); self->render_data.capacity = 64; self->render_data.item = calloc(self->render_data.capacity, sizeof(self->render_data.item[0])); @@ -77,8 +77,10 @@ grman_alloc(void) { PyErr_NoMemory(); Py_CLEAR(self); return NULL; } - self->disk_cache = create_disk_cache(); - if (!self->disk_cache) { Py_CLEAR(self); return NULL; } + if (!for_paused_rendering) { + self->disk_cache = create_disk_cache(); + if (!self->disk_cache) { Py_CLEAR(self); return NULL; } + } return self; } @@ -113,6 +115,12 @@ clear_texture_ref(TextureRef **x) { return NULL; } +static TextureRef* +incref_texture_ref(TextureRef *ref) { + if (ref) ref->refcnt++; + return ref; +} + static TextureRef* new_texture_ref(void) { TextureRef *ans = malloc(sizeof(TextureRef)); @@ -128,18 +136,20 @@ texture_id_for_img(Image *img) { static void free_image_resources(GraphicsManager *self, Image *img) { clear_texture_ref(&img->texture); - ImageAndFrame key = { .image_id=img->internal_id, .frame_id = img->root_frame.id }; - if (!remove_from_cache(self, key) && PyErr_Occurred()) PyErr_Print(); - for (unsigned i = 0; i < img->extra_framecnt; i++) { - key.frame_id = img->extra_frames[i].id; + if (self->disk_cache) { + ImageAndFrame key = { .image_id=img->internal_id, .frame_id = img->root_frame.id }; if (!remove_from_cache(self, key) && PyErr_Occurred()) PyErr_Print(); + for (unsigned i = 0; i < img->extra_framecnt; i++) { + key.frame_id = img->extra_frames[i].id; + if (!remove_from_cache(self, key) && PyErr_Occurred()) PyErr_Print(); + } } if (img->extra_frames) { free(img->extra_frames); img->extra_frames = NULL; } free_refs_data(img); - self->used_storage -= img->used_storage; + self->used_storage = img->used_storage <= self->used_storage ? self->used_storage - img->used_storage : 0; } static void @@ -150,7 +160,7 @@ free_image(GraphicsManager *self, Image *img) { } static void -dealloc(GraphicsManager* self) { +free_all_images(GraphicsManager *self) { if (self->images) { Image *img, *tmp; HASH_ITER(hh, self->images, img, tmp) { @@ -158,6 +168,11 @@ dealloc(GraphicsManager* self) { } self->images = NULL; } +} + +static void +dealloc(GraphicsManager* self) { + free_all_images(self); free(self->render_data.item); Py_CLEAR(self->disk_cache); Py_TYPE(self)->tp_free((PyObject*)self); @@ -222,6 +237,38 @@ remove_images(GraphicsManager *self, bool(*predicate)(Image*), id_type skip_imag } } +void +grman_pause_rendering(GraphicsManager *self, GraphicsManager *dest) { + make_window_context_current(dest->window_id); + free_all_images(dest); dest->images = NULL; + dest->render_data.count = 0; + if (self == NULL) return; + dest->window_id = self->window_id; + dest->layers_dirty = true; + dest->last_scrolled_by = 0; + + Image *img, *tmpimg; + + HASH_ITER(hh, self->images, img, tmpimg) { + Image *clone = calloc(1, sizeof(Image)); + if (!clone) continue; + memcpy(clone, img, sizeof(*clone)); + clone->extra_frames = NULL; + if (img->refs) { + clone->refs = NULL; + ImageRef *ref, *tmpref; + HASH_ITER(hh, img->refs, ref, tmpref) { + ImageRef *cr = malloc(sizeof(ImageRef)); + if (cr) { + memcpy(cr, ref, sizeof(*cr)); + HASH_ADD(hh, clone->refs, internal_id, sizeof(cr->internal_id), cr); + } + } + } + HASH_ADD(hh, dest->images, internal_id, sizeof(clone->internal_id), clone); + clone->texture = incref_texture_ref(img->texture); + } +} // Loading image data {{{ @@ -2164,7 +2211,7 @@ grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint // Boilerplate {{{ static PyObject * new(PyTypeObject UNUSED *type, PyObject UNUSED *args, PyObject UNUSED *kwds) { - PyObject *ans = (PyObject*)grman_alloc(); + PyObject *ans = (PyObject*)grman_alloc(false); if (ans == NULL) PyErr_NoMemory(); return ans; } diff --git a/kitty/graphics.h b/kitty/graphics.h index 42664ab8a..43065c4dd 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -180,7 +180,7 @@ gl_pos_y(const unsigned int px_from_top_margin, const unsigned int viewport_size } -GraphicsManager* grman_alloc(void); +GraphicsManager* grman_alloc(bool for_paused_rendering); void grman_clear(GraphicsManager*, bool, CellPixelSize fg); const char* grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty, CellPixelSize fg); Image* grman_put_cell_image(GraphicsManager *self, uint32_t row, uint32_t col, uint32_t image_id, uint32_t placement_id, uint32_t x, uint32_t y, uint32_t w, uint32_t h, CellPixelSize cell); @@ -196,3 +196,4 @@ bool png_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, u bool png_from_data(void *png_data, size_t png_data_sz, const char *path_for_error_messages, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz); bool scan_active_animations(GraphicsManager *self, const monotonic_t now, monotonic_t *minimum_gap, bool os_window_context_set); void scale_rendered_graphic(ImageRenderData*, float xstart, float ystart, float x_scale, float y_scale); +void grman_pause_rendering(GraphicsManager *self, GraphicsManager *dest); diff --git a/kitty/screen.c b/kitty/screen.c index 51d013577..0db45f90d 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -127,8 +127,8 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) { self->main_linebuf = alloc_linebuf(lines, columns); self->alt_linebuf = alloc_linebuf(lines, columns); self->linebuf = self->main_linebuf; self->historybuf = alloc_historybuf(MAX(scrollback, lines), columns, OPT(scrollback_pager_history_size)); - self->main_grman = grman_alloc(); - self->alt_grman = grman_alloc(); + self->main_grman = grman_alloc(false); + self->alt_grman = grman_alloc(false); self->active_hyperlink_id = 0; self->grman = self->main_grman; @@ -474,6 +474,7 @@ dealloc(Screen* self) { Py_CLEAR(self->overlay_line.overlay_text); PyMem_Free(self->main_tabstops); Py_CLEAR(self->paused_rendering.linebuf); + Py_CLEAR(self->paused_rendering.grman); free(self->selections.items); free(self->url_ranges.items); free(self->paused_rendering.url_ranges.items); @@ -2391,9 +2392,13 @@ screen_pause_rendering(Screen *self, bool pause, int for_in_ms) { self->is_dirty = true; // ensure selection data is updated on GPU self->selections.last_rendered_count = SIZE_MAX; self->url_ranges.last_rendered_count = SIZE_MAX; + // free grman data + grman_pause_rendering(NULL, self->paused_rendering.grman); return true; } if (self->paused_rendering.expires_at) return false; + if (!self->paused_rendering.grman) self->paused_rendering.grman = grman_alloc(true); + if (!self->paused_rendering.grman) return false; if (for_in_ms <= 0) for_in_ms = 2000; self->paused_rendering.expires_at = monotonic() + ms_to_monotonic_t(for_in_ms); self->paused_rendering.inverted = self->modes.mDECSCNM; @@ -2415,6 +2420,7 @@ screen_pause_rendering(Screen *self, bool pause, int for_in_ms) { } copy_selections(&self->paused_rendering.selections, &self->selections); copy_selections(&self->paused_rendering.url_ranges, &self->url_ranges); + grman_pause_rendering(self->grman, self->paused_rendering.grman); return true; } diff --git a/kitty/screen.h b/kitty/screen.h index edebe8f6b..4f6cda922 100644 --- a/kitty/screen.h +++ b/kitty/screen.h @@ -157,6 +157,7 @@ typedef struct { bool inverted, cell_data_updated, cursor_visible; unsigned int scrolled_by; LineBuf *linebuf; + GraphicsManager *grman; Selections selections, url_ranges; } paused_rendering; } Screen; diff --git a/kitty/shaders.c b/kitty/shaders.c index 88dbdcc82..e02aebf47 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -419,15 +419,18 @@ cell_prepare_to_render(ssize_t vao_idx, Screen *screen, GLfloat xstart, GLfloat changed = true; \ } +#define update_graphics_data(grman) \ + grman_update_layers(grman, screen->scrolled_by, xstart, ystart, dx, dy, screen->columns, screen->lines, screen->cell_size) + if (screen->paused_rendering.expires_at) { - if (!screen->paused_rendering.cell_data_updated) update_selection_data; + if (!screen->paused_rendering.cell_data_updated) { + update_selection_data; update_graphics_data(screen->paused_rendering.grman); + } screen->paused_rendering.cell_data_updated = true; screen->last_rendered.scrolled_by = screen->paused_rendering.scrolled_by; } else { if (screen->reload_all_gpu_data || screen_resized || screen_is_selection_dirty(screen)) update_selection_data; - if (grman_update_layers(screen->grman, screen->scrolled_by, xstart, ystart, dx, dy, screen->columns, screen->lines, screen->cell_size)) { - changed = true; - } + if (update_graphics_data(screen->grman)) changed = true; screen->last_rendered.scrolled_by = screen->scrolled_by; } #undef update_selection_data @@ -563,11 +566,12 @@ static void draw_cells_simple(ssize_t vao_idx, Screen *screen, const CellRenderData *crd, bool is_semi_transparent) { bind_program(CELL_PROGRAM); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); - if (screen->grman->render_data.count) { + GraphicsManager *grman = screen->paused_rendering.expires_at && screen->paused_rendering.grman ? screen->paused_rendering.grman : screen->grman; + if (grman->render_data.count) { glEnable(GL_BLEND); int program = GRAPHICS_PROGRAM; if (is_semi_transparent) { BLEND_PREMULT; program = GRAPHICS_PREMULT_PROGRAM; } else { BLEND_ONTO_OPAQUE; } - draw_graphics(program, vao_idx, screen->grman->render_data.item, 0, screen->grman->render_data.count, viewport_for_cells(crd)); + draw_graphics(program, vao_idx, grman->render_data.item, 0, grman->render_data.count, viewport_for_cells(crd)); glDisable(GL_BLEND); } } @@ -804,20 +808,21 @@ draw_cells_interleaved(ssize_t vao_idx, Screen *screen, OSWindow *w, const CellR BLEND_ONTO_OPAQUE; } - if (screen->grman->num_of_below_refs || has_bgimage(w) || wl) { + GraphicsManager *grman = screen->paused_rendering.expires_at && screen->paused_rendering.grman ? screen->paused_rendering.grman : screen->grman; + if (grman->num_of_below_refs || has_bgimage(w) || wl) { if (wl) { draw_window_logo(vao_idx, w, wl, crd); BLEND_ONTO_OPAQUE; } - if (screen->grman->num_of_below_refs) draw_graphics( - GRAPHICS_PROGRAM, vao_idx, screen->grman->render_data.item, 0, screen->grman->num_of_below_refs, viewport_for_cells(crd)); + if (grman->num_of_below_refs) draw_graphics( + GRAPHICS_PROGRAM, vao_idx, grman->render_data.item, 0, grman->num_of_below_refs, viewport_for_cells(crd)); bind_program(CELL_BG_PROGRAM); // draw background for non-default bg cells glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].uniforms.draw_bg_bitfield, 2); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); } - if (screen->grman->num_of_negative_refs) draw_graphics(GRAPHICS_PROGRAM, vao_idx, screen->grman->render_data.item, screen->grman->num_of_below_refs, screen->grman->num_of_negative_refs, viewport_for_cells(crd)); + if (grman->num_of_negative_refs) draw_graphics(GRAPHICS_PROGRAM, vao_idx, grman->render_data.item, grman->num_of_below_refs, grman->num_of_negative_refs, viewport_for_cells(crd)); bind_program(CELL_SPECIAL_PROGRAM); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); @@ -827,7 +832,7 @@ draw_cells_interleaved(ssize_t vao_idx, Screen *screen, OSWindow *w, const CellR glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); BLEND_ONTO_OPAQUE; - if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PROGRAM, vao_idx, screen->grman->render_data.item, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs, viewport_for_cells(crd)); + if (grman->num_of_positive_refs) draw_graphics(GRAPHICS_PROGRAM, vao_idx, grman->render_data.item, grman->num_of_negative_refs + grman->num_of_below_refs, grman->num_of_positive_refs, viewport_for_cells(crd)); glDisable(GL_BLEND); } @@ -848,13 +853,14 @@ draw_cells_interleaved_premult(ssize_t vao_idx, Screen *screen, OSWindow *os_win glEnable(GL_BLEND); BLEND_PREMULT; - if (screen->grman->num_of_below_refs || has_bgimage(os_window) || wl) { + GraphicsManager *grman = screen->paused_rendering.expires_at && screen->paused_rendering.grman ? screen->paused_rendering.grman : screen->grman; + if (grman->num_of_below_refs || has_bgimage(os_window) || wl) { if (wl) { draw_window_logo(vao_idx, os_window, wl, crd); BLEND_PREMULT; } - if (screen->grman->num_of_below_refs) draw_graphics( - GRAPHICS_PREMULT_PROGRAM, vao_idx, screen->grman->render_data.item, 0, screen->grman->num_of_below_refs, viewport_for_cells(crd)); + if (grman->num_of_below_refs) draw_graphics( + GRAPHICS_PREMULT_PROGRAM, vao_idx, grman->render_data.item, 0, grman->num_of_below_refs, viewport_for_cells(crd)); bind_program(CELL_BG_PROGRAM); // Draw background for non-default bg cells glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].uniforms.draw_bg_bitfield, 2); @@ -865,8 +871,8 @@ draw_cells_interleaved_premult(ssize_t vao_idx, Screen *screen, OSWindow *os_win glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); } - if (screen->grman->num_of_negative_refs) { - draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, screen->grman->render_data.item, screen->grman->num_of_below_refs, screen->grman->num_of_negative_refs, viewport_for_cells(crd)); + if (grman->num_of_negative_refs) { + draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, grman->render_data.item, grman->num_of_below_refs, grman->num_of_negative_refs, viewport_for_cells(crd)); } bind_program(CELL_SPECIAL_PROGRAM); @@ -875,7 +881,7 @@ draw_cells_interleaved_premult(ssize_t vao_idx, Screen *screen, OSWindow *os_win bind_program(CELL_FG_PROGRAM); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); - if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, screen->grman->render_data.item, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs, viewport_for_cells(crd)); + if (grman->num_of_positive_refs) draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, grman->render_data.item, grman->num_of_negative_refs + grman->num_of_below_refs, grman->num_of_positive_refs, viewport_for_cells(crd)); if (!has_bgimage(os_window)) glDisable(GL_BLEND); } @@ -957,15 +963,16 @@ draw_cells(ssize_t vao_idx, const WindowRenderData *srd, OSWindow *os_window, bo set_on_gpu_state(window->window_logo.instance, true); } else wl = NULL; ImageRenderData *previous_graphics_render_data = NULL; - if (os_window->live_resize.in_progress && screen->grman->render_data.count && (crd.x_ratio != 1 || crd.y_ratio != 1)) { - previous_graphics_render_data = malloc(sizeof(previous_graphics_render_data[0]) * screen->grman->render_data.capacity); + GraphicsManager *grman = screen->paused_rendering.expires_at && screen->paused_rendering.grman ? screen->paused_rendering.grman : screen->grman; + if (os_window->live_resize.in_progress && grman->render_data.count && (crd.x_ratio != 1 || crd.y_ratio != 1)) { + previous_graphics_render_data = malloc(sizeof(previous_graphics_render_data[0]) * grman->render_data.capacity); if (previous_graphics_render_data) { - memcpy(previous_graphics_render_data, screen->grman->render_data.item, sizeof(previous_graphics_render_data[0]) * screen->grman->render_data.count); - for (size_t i = 0; i < screen->grman->render_data.count; i++) - scale_rendered_graphic(screen->grman->render_data.item + i, srd->xstart, srd->ystart, crd.x_ratio, crd.y_ratio); + memcpy(previous_graphics_render_data, grman->render_data.item, sizeof(previous_graphics_render_data[0]) * grman->render_data.count); + for (size_t i = 0; i < grman->render_data.count; i++) + scale_rendered_graphic(grman->render_data.item + i, srd->xstart, srd->ystart, crd.x_ratio, crd.y_ratio); } } - has_underlying_image |= screen->grman->num_of_below_refs > 0 || screen->grman->num_of_negative_refs > 0; + has_underlying_image |= grman->num_of_below_refs > 0 || grman->num_of_negative_refs > 0; if (os_window->is_semi_transparent) { if (has_underlying_image) draw_cells_interleaved_premult(vao_idx, screen, os_window, &crd, wl); else draw_cells_simple(vao_idx, screen, &crd, os_window->is_semi_transparent); @@ -982,8 +989,8 @@ draw_cells(ssize_t vao_idx, const WindowRenderData *srd, OSWindow *os_window, bo if (window && screen->display_window_char) draw_window_number(os_window, screen, &crd, window); if (OPT(show_hyperlink_targets) && window && screen->current_hyperlink_under_mouse.id && !is_mouse_hidden(os_window)) draw_hyperlink_target(os_window, screen, &crd, window); if (previous_graphics_render_data) { - free(screen->grman->render_data.item); - screen->grman->render_data.item = previous_graphics_render_data; + free(grman->render_data.item); + grman->render_data.item = previous_graphics_render_data; } } // }}}