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:
parent
597267d1d0
commit
00edb6058e
114
kitty/graphics.c
114
kitty/graphics.c
@ -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,30 +735,41 @@ 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++) {
|
||||||
r.top = y0 - ref->start_row * dy - dy * (float)ref->cell_y_offset / (float)cell.height;
|
img = self->images + i;
|
||||||
if (ref->num_rows > 0) r.bottom = y0 - (ref->start_row + (int32_t)ref->num_rows) * dy;
|
bool was_drawn = img->is_drawn;
|
||||||
else r.bottom = r.top - screen_height * (float)ref->src_height / screen_height_px;
|
img->is_drawn = false;
|
||||||
if (r.top <= screen_bottom || r.bottom >= screen_top) continue; // not visible
|
|
||||||
|
|
||||||
r.left = screen_left + ref->start_column * dx + dx * (float)ref->cell_x_offset / (float) cell.width;
|
for (j = 0; j < img->refcnt; j++) { ref = img->refs + j;
|
||||||
if (ref->num_cols > 0) r.right = screen_left + (ref->start_column + (int32_t)ref->num_cols) * dx;
|
r.top = y0 - ref->start_row * dy - dy * (float)ref->cell_y_offset / (float)cell.height;
|
||||||
else r.right = r.left + screen_width * (float)ref->src_width / screen_width_px;
|
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;
|
||||||
|
if (r.top <= screen_bottom || r.bottom >= screen_top) continue; // not visible
|
||||||
|
|
||||||
if (ref->z_index < ((int32_t)INT32_MIN/2))
|
r.left = screen_left + ref->start_column * dx + dx * (float)ref->cell_x_offset / (float) cell.width;
|
||||||
self->num_of_below_refs++;
|
if (ref->num_cols > 0) r.right = screen_left + (ref->start_column + (int32_t)ref->num_cols) * dx;
|
||||||
else if (ref->z_index < 0)
|
else r.right = r.left + screen_width * (float)ref->src_width / screen_width_px;
|
||||||
self->num_of_negative_refs++;
|
|
||||||
else
|
if (ref->z_index < ((int32_t)INT32_MIN/2))
|
||||||
self->num_of_positive_refs++;
|
self->num_of_below_refs++;
|
||||||
ensure_space_for(self, render_data, ImageRenderData, self->count + 1, capacity, 64, true);
|
else if (ref->z_index < 0)
|
||||||
ImageRenderData *rd = self->render_data + self->count;
|
self->num_of_negative_refs++;
|
||||||
zero_at_ptr(rd);
|
else
|
||||||
set_vertex_data(rd, ref, &r);
|
self->num_of_positive_refs++;
|
||||||
self->count++;
|
ensure_space_for(self, render_data, ImageRenderData, self->count + 1, capacity, 64, true);
|
||||||
rd->z_index = ref->z_index; rd->image_id = img->internal_id;
|
ImageRenderData *rd = self->render_data + self->count;
|
||||||
rd->texture_id = img->texture_id;
|
zero_at_ptr(rd);
|
||||||
}}
|
set_vertex_data(rd, ref, &r);
|
||||||
|
self->count++;
|
||||||
|
rd->z_index = ref->z_index; rd->image_id = img->internal_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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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 {{{
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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__))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user