Implement paused rendering for graphics

This commit is contained in:
Kovid Goyal 2024-01-04 08:04:39 +05:30
parent 7b963a2372
commit f45cd87488
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 100 additions and 38 deletions

View File

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

View File

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

View File

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

View File

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

View File

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