From 35f3312a1ed1cb1d4c107df3083291e94d22f393 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 27 Jan 2021 15:05:08 +0530 Subject: [PATCH] Use shorter cache keys --- kitty/graphics.c | 58 ++++++++++++++++++++++++++++++++--------- kitty/graphics.h | 3 --- kitty_tests/graphics.py | 2 ++ 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/kitty/graphics.c b/kitty/graphics.c index 5c1c5ba4e..df6a4550e 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -24,6 +24,42 @@ PyTypeObject GraphicsManager_Type; #define DEFAULT_STORAGE_LIMIT 320u * (1024u * 1024u) #define REPORT_ERROR(...) { log_error(__VA_ARGS__); } +#define CACHE_KEY_BUFFER_SIZE 32 + +static inline size_t +cache_key(const ImageAndFrame x, char *key) { + if (x.frame_idx) return snprintf(key, CACHE_KEY_BUFFER_SIZE, "%llx:%x", x.image_id, x.frame_idx); + return snprintf(key, CACHE_KEY_BUFFER_SIZE, "%llx:", x.image_id); +} +#define CK(x) key, cache_key(x, key) + +static inline bool +add_to_cache(GraphicsManager *self, const ImageAndFrame x, const void *data, const size_t sz) { + char key[CACHE_KEY_BUFFER_SIZE]; + return add_to_disk_cache(self->disk_cache, CK(x), data, sz); +} + +static inline bool +remove_from_cache(GraphicsManager *self, const ImageAndFrame x) { + char key[CACHE_KEY_BUFFER_SIZE]; + return remove_from_disk_cache(self->disk_cache, CK(x)); +} + +static inline PyObject* +read_from_cache_python(const GraphicsManager *self, const ImageAndFrame x) { + char key[CACHE_KEY_BUFFER_SIZE]; + return read_from_disk_cache_python(self->disk_cache, CK(x)); +} + +static inline bool +read_from_cache(const GraphicsManager *self, const ImageAndFrame x, void **data, size_t *sz) { + char key[CACHE_KEY_BUFFER_SIZE]; + return read_from_disk_cache_simple(self->disk_cache, CK(x), data, sz); +} + +static inline size_t +cache_size(const GraphicsManager *self) { return disk_cache_total_size(self->disk_cache); } +#undef CK static bool send_to_gpu = true; @@ -62,9 +98,7 @@ free_image(GraphicsManager *self, Image *img) { if (img->texture_id) free_texture(&img->texture_id); ImageAndFrame key = { .image_id=img->internal_id }; for (key.frame_idx = 0; key.frame_idx <= img->extra_framecnt; key.frame_idx++) { - if (!remove_from_disk_cache(self->disk_cache, &key, sizeof(key))) { - if (PyErr_Occurred()) PyErr_Print(); - } + if (!remove_from_cache(self, key) && PyErr_Occurred()) PyErr_Print(); } if (img->extra_frames) { free(img->extra_frames); @@ -544,8 +578,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_ if (send_to_gpu) { 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); } - ImageAndFrame key = {.image_id = img->internal_id}; - if (!add_to_disk_cache(self->disk_cache, &key, sizeof(key), img->load_data.data, img->load_data.data_sz)) { + if (!add_to_cache(self, (const ImageAndFrame){.image_id = img->internal_id}, img->load_data.data, img->load_data.data_sz)) { if (PyErr_Occurred()) PyErr_Print(); ABRT("ENOSPC", "Failed to store image data in disk cache"); } @@ -961,9 +994,9 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I FAIL("EINVAL", "Data type for frames must match that of the base image"); if (img->load_data.data_sz < bytes_per_pixel * data_width * data_height) FAIL("ENODATA", "Insufficient image data %zu < %zu", img->load_data.data_sz, bytes_per_pixel * data_width, data_height); - if (is_new_frame && disk_cache_total_size(self->disk_cache) + expected_data_sz > self->storage_limit * 5) { + if (is_new_frame && cache_size(self) + expected_data_sz > self->storage_limit * 5) { remove_images(self, trim_predicate, img->internal_id); - if (is_new_frame && disk_cache_total_size(self->disk_cache) + expected_data_sz > self->storage_limit * 5) + if (is_new_frame && cache_size(self) + expected_data_sz > self->storage_limit * 5) FAIL("ENOSPC", "Cache size exceeded cannot add new frames"); } @@ -972,7 +1005,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I if (is_new_frame) { if (g->num_cells) { ImageAndFrame other = { .image_id = img->internal_id, .frame_idx = g->num_cells - 1 }; - if (!read_from_disk_cache_simple(self->disk_cache, &other, sizeof(other), &base_data, &data_sz)) { + if (!read_from_cache(self, other, &base_data, &data_sz)) { FAIL("ENODATA", "No data for frame with number: %u found in image: %u", g->num_cells, img->client_id); } } else { @@ -981,7 +1014,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I data_sz = expected_data_sz; } } else { - if (!read_from_disk_cache_simple(self->disk_cache, &key, sizeof(key), &base_data, &data_sz)) { + if (!read_from_cache(self, key, &base_data, &data_sz)) { FAIL("ENODATA", "No data for frame with number: %u found in image: %u", frame_number, img->client_id); } } @@ -1005,7 +1038,7 @@ handle_animation_frame_load_command(GraphicsManager *self, GraphicsCommand *g, I #undef FAIL free_load_data(&img->load_data); - bool added = add_to_disk_cache(self->disk_cache, &key, sizeof(key), base_data, data_sz); + bool added = add_to_cache(self, key, base_data, data_sz); free(base_data); if (!added) { PyErr_Print(); @@ -1132,7 +1165,8 @@ image_as_dict(GraphicsManager *self, Image *img) { for (unsigned i = 0; i < img->extra_framecnt; i++) { key.frame_idx = i + 1; PyTuple_SET_ITEM(frames, i, Py_BuildValue( - "{sI sN}", "gap", img->extra_frames[i].gap, "data", read_from_disk_cache_python(self->disk_cache, &key, sizeof(key)))); + "{sI sN}", "gap", img->extra_frames[i].gap, "data", read_from_cache_python(self, key))); + if (PyErr_Occurred()) return NULL; } key.frame_idx = 0; return Py_BuildValue("{sI sI sI sI sK sI sI sO sO sI sI sO sN sN}", @@ -1141,7 +1175,7 @@ image_as_dict(GraphicsManager *self, Image *img) { "is_4byte_aligned", img->is_4byte_aligned ? Py_True : Py_False, U(current_frame_index), U(loop_delay), "animation_enabled", img->animation_enabled ? Py_True : Py_False, - "data", read_from_disk_cache_python(self->disk_cache, &key, sizeof(key)), + "data", read_from_cache_python(self, key), "extra_frames", frames ); #undef U diff --git a/kitty/graphics.h b/kitty/graphics.h index 4d44ce0c7..cff6b36ad 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -7,7 +7,6 @@ #pragma once #include "data-types.h" #include "monotonic.h" -#include typedef struct { unsigned char action, transmission_type, compressed, delete_action; @@ -82,8 +81,6 @@ typedef struct { id_type image_id; uint32_t frame_idx; } ImageAndFrame; -static_assert(sizeof(ImageAndFrame) != sizeof(id_type) + sizeof(uint32_t), - "Padding not allowed in ImageAndFrame because it is used as a cache key and padding is un-initialized"); typedef struct { PyObject_HEAD diff --git a/kitty_tests/graphics.py b/kitty_tests/graphics.py index 04a7c52bf..add4f50f2 100644 --- a/kitty_tests/graphics.py +++ b/kitty_tests/graphics.py @@ -612,6 +612,8 @@ class TestGraphics(BaseTest): # simple new frame t(payload='2' * 36) + img = g.image_for_client_id(1) + self.assertEqual(img['extra_frames'], ({'gap': 40, 'data': b'2' * 36},)) # self.ae(g.image_count, 0) # self.assertEqual(g.disk_cache.total_size, 0)