Start work on implementing frame transitions

Also ensure that the correct OS Window is current when uploading images
to GPU.
This commit is contained in:
Kovid Goyal 2021-01-30 12:43:30 +05:30
parent 597267d1d0
commit 00edb6058e
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 115 additions and 44 deletions

View File

@ -61,8 +61,6 @@ cache_size(const GraphicsManager *self) { return disk_cache_total_size(self->dis
#undef CK #undef CK
static bool send_to_gpu = true;
GraphicsManager* GraphicsManager*
grman_alloc() { grman_alloc() {
GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0); GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0);
@ -525,6 +523,14 @@ initialize_load_data(GraphicsManager *self, const GraphicsCommand *g, Image *img
} }
#define MAX_IMAGE_DIMENSION 10000u #define MAX_IMAGE_DIMENSION 10000u
static void
upload_to_gpu(GraphicsManager *self, Image *img, void *data) {
if (self->window_id && !self->context_made_current_for_this_command) {
if (make_window_context_current(self->window_id)) {
self->context_made_current_for_this_command = true;
send_image_to_gpu(&img->texture_id, data, img->width, img->height, img->is_opaque, img->is_4byte_aligned, false, REPEAT_CLAMP);
}}
}
static Image* static Image*
handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, bool *is_dirty, uint32_t iid) { handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, bool *is_dirty, uint32_t iid) {
@ -543,6 +549,8 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
if (existing) { if (existing) {
free_load_data(&img->load_data); free_load_data(&img->load_data);
img->data_loaded = false; img->data_loaded = false;
img->is_drawn = false;
img->current_frame_shown_at = 0;
free_refs_data(img); free_refs_data(img);
*is_dirty = true; *is_dirty = true;
self->layers_dirty = true; self->layers_dirty = true;
@ -576,9 +584,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
if (img->data_loaded) { if (img->data_loaded) {
img->is_opaque = img->load_data.is_opaque; img->is_opaque = img->load_data.is_opaque;
img->is_4byte_aligned = img->load_data.is_4byte_aligned; img->is_4byte_aligned = img->load_data.is_4byte_aligned;
if (send_to_gpu) { upload_to_gpu(self, img, img->load_data.data);
send_image_to_gpu(&img->texture_id, img->load_data.data, img->width, img->height, img->is_opaque, img->is_4byte_aligned, false, REPEAT_CLAMP);
}
if (img->root_frame.id) remove_from_cache(self, (const ImageAndFrame){.image_id=img->internal_id, .frame_id=img->root_frame.id}); if (img->root_frame.id) remove_from_cache(self, (const ImageAndFrame){.image_id=img->internal_id, .frame_id=img->root_frame.id});
img->root_frame.id = ++img->frame_id_counter; img->root_frame.id = ++img->frame_id_counter;
if (!add_to_cache(self, (const ImageAndFrame){.image_id = img->internal_id, .frame_id=img->root_frame.id}, img->load_data.data, img->load_data.data_sz)) { if (!add_to_cache(self, (const ImageAndFrame){.image_id = img->internal_id, .frame_id=img->root_frame.id}, img->load_data.data, img->load_data.data_sz)) {
@ -729,7 +735,12 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree
// Iterate over all visible refs and create render data // Iterate over all visible refs and create render data
self->count = 0; self->count = 0;
for (i = 0; i < self->image_count; i++) { img = self->images + i; for (j = 0; j < img->refcnt; j++) { ref = img->refs + j; for (i = 0; i < self->image_count; i++) {
img = self->images + i;
bool was_drawn = img->is_drawn;
img->is_drawn = false;
for (j = 0; j < img->refcnt; j++) { ref = img->refs + j;
r.top = y0 - ref->start_row * dy - dy * (float)ref->cell_y_offset / (float)cell.height; r.top = y0 - ref->start_row * dy - dy * (float)ref->cell_y_offset / (float)cell.height;
if (ref->num_rows > 0) r.bottom = y0 - (ref->start_row + (int32_t)ref->num_rows) * dy; if (ref->num_rows > 0) r.bottom = y0 - (ref->start_row + (int32_t)ref->num_rows) * dy;
else r.bottom = r.top - screen_height * (float)ref->src_height / screen_height_px; else r.bottom = r.top - screen_height * (float)ref->src_height / screen_height_px;
@ -752,7 +763,13 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree
self->count++; self->count++;
rd->z_index = ref->z_index; rd->image_id = img->internal_id; rd->z_index = ref->z_index; rd->image_id = img->internal_id;
rd->texture_id = img->texture_id; rd->texture_id = img->texture_id;
}} img->is_drawn = true;
}
if (img->is_drawn && !was_drawn && img->animation_enabled && img->extra_framecnt) {
self->has_images_needing_animation = true;
// TODO: rescan animation timeouts
}
}
if (!self->count) return false; if (!self->count) return false;
// Sort visible refs in draw order (z-index, img) // Sort visible refs in draw order (z-index, img)
#define lt(a, b) ( (a)->z_index < (b)->z_index || ((a)->z_index == (b)->z_index && (a)->image_id < (b)->image_id) ) #define lt(a, b) ( (a)->z_index < (b)->z_index || ((a)->z_index == (b)->z_index && (a)->image_id < (b)->image_id) )
@ -798,10 +815,9 @@ update_current_frame(GraphicsManager *self, Image *img, void *data) {
return; return;
} }
} }
if (send_to_gpu) { upload_to_gpu(self, img, data);
send_image_to_gpu(&img->texture_id, data, img->width, img->height, img->is_opaque, img->is_4byte_aligned, false, REPEAT_CLAMP);
}
if (needs_load) free(data); if (needs_load) free(data);
img->current_frame_shown_at = monotonic();
} }
static Image* static Image*
@ -982,9 +998,40 @@ handle_animation_control_command(GraphicsManager *self, bool *is_dirty, const Gr
} }
} }
if (g->_animation_enabled) { if (g->_animation_enabled) {
bool was_enabled = img->animation_enabled;
img->animation_enabled = g->_animation_enabled == 1; img->animation_enabled = g->_animation_enabled == 1;
if (img->animation_enabled) {
self->has_images_needing_animation = true;
if (!was_enabled) img->current_frame_shown_at = monotonic();
// TODO: schedule animation rescan
} }
} }
}
bool
scan_active_animations(GraphicsManager *self, const monotonic_t now, monotonic_t *minimum_gap) {
bool dirtied = false;
*minimum_gap = MONOTONIC_T_MAX;
if (!self->has_images_needing_animation) return dirtied;
self->has_images_needing_animation = false;
for (size_t i = self->image_count; i-- > 0;) {
Image *img = self->images + i;
if (img->animation_enabled && img->extra_framecnt && img->is_drawn) {
self->has_images_needing_animation = true;
Frame *next_frame = img->current_frame_index + 1 < img->extra_framecnt ? img->extra_frames + img->current_frame_index + 1 : &img->root_frame;
monotonic_t next_frame_at = img->current_frame_shown_at + next_frame->gap;
if (now >= next_frame_at) {
dirtied = true;
img->current_frame_index = (img->current_frame_index + 1) % (img->extra_framecnt + 1);
update_current_frame(self, img, NULL);
next_frame = img->current_frame_index + 1 < img->extra_framecnt ? img->extra_frames + img->current_frame_index + 1 : &img->root_frame;
}
next_frame_at = img->current_frame_shown_at + next_frame->gap;
if (next_frame_at - now < *minimum_gap) *minimum_gap = next_frame_at - now;
}
}
return dirtied;
}
// }}} // }}}
// Image lifetime/scrolling {{{ // Image lifetime/scrolling {{{
@ -1203,6 +1250,7 @@ const char*
grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty, CellPixelSize cell) { grman_handle_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, Cursor *c, bool *is_dirty, CellPixelSize cell) {
const char *ret = NULL; const char *ret = NULL;
command_response[0] = 0; command_response[0] = 0;
self->context_made_current_for_this_command = false;
if (g->id && g->image_number) { if (g->id && g->image_number) {
set_command_failed_response("EINVAL", "Must not specify both image id and image number"); set_command_failed_response("EINVAL", "Must not specify both image id and image number");
@ -1343,11 +1391,6 @@ W(shm_unlink) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
W(set_send_to_gpu) {
send_to_gpu = PyObject_IsTrue(args) ? true : false;
Py_RETURN_NONE;
}
W(update_layers) { W(update_layers) {
unsigned int scrolled_by, sx, sy; float xstart, ystart, dx, dy; unsigned int scrolled_by, sx, sy; float xstart, ystart, dx, dy;
CellPixelSize cell; CellPixelSize cell;
@ -1396,7 +1439,6 @@ PyTypeObject GraphicsManager_Type = {
static PyMethodDef module_methods[] = { static PyMethodDef module_methods[] = {
M(shm_write, METH_VARARGS), M(shm_write, METH_VARARGS),
M(shm_unlink, METH_VARARGS), M(shm_unlink, METH_VARARGS),
M(set_send_to_gpu, METH_O),
{NULL, NULL, 0, NULL} /* Sentinel */ {NULL, NULL, 0, NULL} /* Sentinel */
}; };

View File

@ -43,8 +43,7 @@ typedef struct {
} ImageRef; } ImageRef;
typedef struct { typedef struct {
uint32_t gap; uint32_t gap, id;
uint32_t id;
} Frame; } Frame;
@ -61,7 +60,8 @@ typedef struct {
size_t refcnt, refcap, extra_framecnt; size_t refcnt, refcap, extra_framecnt;
monotonic_t atime; monotonic_t atime;
size_t used_storage; size_t used_storage;
bool is_opaque, is_4byte_aligned, animation_enabled; bool is_opaque, is_4byte_aligned, animation_enabled, is_drawn;
monotonic_t current_frame_shown_at;
} Image; } Image;
typedef struct { typedef struct {
@ -98,6 +98,8 @@ typedef struct {
unsigned int last_scrolled_by; unsigned int last_scrolled_by;
size_t used_storage; size_t used_storage;
PyObject *disk_cache; PyObject *disk_cache;
bool has_images_needing_animation, context_made_current_for_this_command;
id_type window_id;
} GraphicsManager; } GraphicsManager;
@ -116,3 +118,4 @@ void grman_resize(GraphicsManager*, index_type, index_type, index_type, index_ty
void grman_rescale(GraphicsManager *self, CellPixelSize fg); void grman_rescale(GraphicsManager *self, CellPixelSize fg);
void gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height); void gpu_data_for_centered_image(ImageRenderData *ans, unsigned int screen_width_px, unsigned int screen_height_px, unsigned int width, unsigned int height);
bool png_path_to_bitmap(const char *path, uint8_t** data, unsigned int* width, unsigned int* height, size_t* sz); bool png_path_to_bitmap(const char *path, 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);

View File

@ -131,6 +131,7 @@ new(PyTypeObject *type, PyObject *args, PyObject UNUSED *kwds) {
) { ) {
Py_CLEAR(self); return NULL; Py_CLEAR(self); return NULL;
} }
self->main_grman->window_id = self->window_id; self->alt_grman->window_id = self->window_id;
self->alt_tabstops = self->main_tabstops + self->columns; self->alt_tabstops = self->main_tabstops + self->columns;
self->tabstops = self->main_tabstops; self->tabstops = self->main_tabstops;
init_tabstops(self->main_tabstops, self->columns); init_tabstops(self->main_tabstops, self->columns);

View File

@ -460,6 +460,34 @@ mark_os_window_for_close(OSWindow* w, CloseRequest cr) {
w->close_request = cr; w->close_request = cr;
} }
static bool
owners_for_window_id(id_type window_id, OSWindow **os_window, Tab **tab) {
if (os_window) *os_window = NULL;
if (tab) *tab = NULL;
for (size_t o = 0; o < global_state.num_os_windows; o++) {
OSWindow *osw = global_state.os_windows + o;
for (size_t t = 0; t < osw->num_tabs; t++) {
Tab *qtab = osw->tabs + t;
for (size_t w = 0; w < qtab->num_windows; w++) {
Window *window = qtab->windows + w;
if (window->id == window_id) {
if (os_window) *os_window = osw;
if (tab) *tab = qtab;
return true;
}}}}
return false;
}
bool
make_window_context_current(id_type window_id) {
OSWindow *os_window;
if (owners_for_window_id(window_id, &os_window, NULL)) {
make_os_window_context_current(os_window);
return true;
}
return false;
}
// Python API {{{ // Python API {{{

View File

@ -229,7 +229,7 @@ void update_os_window_viewport(OSWindow *window, bool);
bool should_os_window_be_rendered(OSWindow* w); bool should_os_window_be_rendered(OSWindow* w);
void wakeup_main_loop(void); void wakeup_main_loop(void);
void swap_window_buffers(OSWindow *w); void swap_window_buffers(OSWindow *w);
void make_window_context_current(OSWindow *w); bool make_window_context_current(id_type);
void hide_mouse(OSWindow *w); void hide_mouse(OSWindow *w);
bool is_mouse_hidden(OSWindow *w); bool is_mouse_hidden(OSWindow *w);
void destroy_os_window(OSWindow *w); void destroy_os_window(OSWindow *w);

View File

@ -15,8 +15,7 @@ from typing import NamedTuple
from kitty.constants import cache_dir from kitty.constants import cache_dir
from kitty.fast_data_types import ( from kitty.fast_data_types import (
load_png_data, parse_bytes, set_send_to_gpu, shm_unlink, shm_write, load_png_data, parse_bytes, shm_unlink, shm_write, xor_data
xor_data
) )
from . import BaseTest from . import BaseTest
@ -26,8 +25,6 @@ try:
except ImportError: except ImportError:
Image = None Image = None
set_send_to_gpu(False)
def relpath(name): def relpath(name):
base = os.path.dirname(os.path.abspath(__file__)) base = os.path.dirname(os.path.abspath(__file__))