Implement sending of image data to GPU
This commit is contained in:
parent
e2a8115328
commit
3cbc20005b
13
kitty/gl.h
13
kitty/gl.h
@ -97,6 +97,19 @@ free_texture_impl(GLuint *tex_id) {
|
|||||||
*tex_id = 0;
|
*tex_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_image_to_gpu_impl(GLuint *tex_id, const void* data, GLsizei width, GLsizei height, bool is_rgb, bool is_4byte_aligned) {
|
||||||
|
if (!(*tex_id)) { glGenTextures(1, tex_id); check_gl(); }
|
||||||
|
glBindTexture(GL_TEXTURE_2D, *tex_id); check_gl();
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, is_4byte_aligned ? 4 : 1); check_gl();
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); check_gl();
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, is_rgb ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE, data); check_gl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
// Programs {{{
|
// Programs {{{
|
||||||
|
|||||||
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
#define REPORT_ERROR(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
|
#define REPORT_ERROR(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
|
||||||
|
|
||||||
|
static bool send_to_gpu = true;
|
||||||
|
|
||||||
GraphicsManager*
|
GraphicsManager*
|
||||||
grman_realloc(GraphicsManager *old, index_type lines, index_type columns) {
|
grman_realloc(GraphicsManager *old, index_type lines, index_type columns) {
|
||||||
GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0);
|
GraphicsManager *self = (GraphicsManager *)GraphicsManager_Type.tp_alloc(&GraphicsManager_Type, 0);
|
||||||
@ -53,6 +55,7 @@ free_load_data(LoadData *ld) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
free_texture_func free_texture = NULL;
|
free_texture_func free_texture = NULL;
|
||||||
|
send_image_to_gpu_func send_image_to_gpu = NULL;
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
free_image(Image *img) {
|
free_image(Image *img) {
|
||||||
@ -306,11 +309,11 @@ remove_images(GraphicsManager *self, bool(*predicate)(Image*)) {
|
|||||||
|
|
||||||
static Image*
|
static Image*
|
||||||
handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, bool *is_dirty) {
|
handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_t *payload, bool *is_dirty) {
|
||||||
#define ABRT(code, ...) { set_add_response(#code, __VA_ARGS__); self->loading_image = 0; return NULL; }
|
#define ABRT(code, ...) { set_add_response(#code, __VA_ARGS__); self->loading_image = 0; if (img) img->data_loaded = false; return NULL; }
|
||||||
#define MAX_DATA_SZ (4 * 100000000)
|
#define MAX_DATA_SZ (4 * 100000000)
|
||||||
has_add_respose = false;
|
has_add_respose = false;
|
||||||
bool existing, init_img = true;
|
bool existing, init_img = true;
|
||||||
Image *img;
|
Image *img = NULL;
|
||||||
unsigned char tt = g->transmission_type ? g->transmission_type : 'd';
|
unsigned char tt = g->transmission_type ? g->transmission_type : 'd';
|
||||||
enum FORMATS { RGB=24, RGBA=32, PNG=100 };
|
enum FORMATS { RGB=24, RGBA=32, PNG=100 };
|
||||||
uint32_t fmt = g->format ? g->format : RGBA;
|
uint32_t fmt = g->format ? g->format : RGBA;
|
||||||
@ -335,6 +338,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
|||||||
case PNG:
|
case PNG:
|
||||||
if (g->data_sz > MAX_DATA_SZ) ABRT(EINVAL, "PNG data size too large");
|
if (g->data_sz > MAX_DATA_SZ) ABRT(EINVAL, "PNG data size too large");
|
||||||
img->load_data.is_4byte_aligned = true;
|
img->load_data.is_4byte_aligned = true;
|
||||||
|
img->load_data.is_rgb = false;
|
||||||
img->load_data.data_sz = g->data_sz ? g->data_sz : 1024 * 100;
|
img->load_data.data_sz = g->data_sz ? g->data_sz : 1024 * 100;
|
||||||
break;
|
break;
|
||||||
case RGB:
|
case RGB:
|
||||||
@ -342,6 +346,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
|||||||
img->load_data.data_sz = g->data_width * g->data_height * (fmt / 8);
|
img->load_data.data_sz = g->data_width * g->data_height * (fmt / 8);
|
||||||
if (!img->load_data.data_sz) ABRT(EINVAL, "Zero width/height not allowed");
|
if (!img->load_data.data_sz) ABRT(EINVAL, "Zero width/height not allowed");
|
||||||
img->load_data.is_4byte_aligned = fmt == RGBA || (img->width % 4 == 0);
|
img->load_data.is_4byte_aligned = fmt == RGBA || (img->width % 4 == 0);
|
||||||
|
img->load_data.is_rgb = fmt == RGB;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ABRT(EINVAL, "Unknown image format: %u", fmt);
|
ABRT(EINVAL, "Unknown image format: %u", fmt);
|
||||||
@ -413,7 +418,6 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ABRT(EINVAL, "Unknown image compression: %c", g->compressed);
|
ABRT(EINVAL, "Unknown image compression: %c", g->compressed);
|
||||||
img->data_loaded = false; return NULL;
|
|
||||||
}
|
}
|
||||||
switch(fmt) {
|
switch(fmt) {
|
||||||
case PNG:
|
case PNG:
|
||||||
@ -428,7 +432,6 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
|||||||
img->load_data.data = img->load_data.buf;
|
img->load_data.data = img->load_data.buf;
|
||||||
if (img->load_data.buf_used < img->load_data.data_sz) {
|
if (img->load_data.buf_used < img->load_data.data_sz) {
|
||||||
ABRT(ENODATA, "Insufficient image data: %zu < %zu", img->load_data.buf_used, img->load_data.data_sz);
|
ABRT(ENODATA, "Insufficient image data: %zu < %zu", img->load_data.buf_used, img->load_data.data_sz);
|
||||||
img->data_loaded = false;
|
|
||||||
}
|
}
|
||||||
if (img->load_data.mapped_file) {
|
if (img->load_data.mapped_file) {
|
||||||
munmap(img->load_data.mapped_file, img->load_data.mapped_file_sz);
|
munmap(img->load_data.mapped_file, img->load_data.mapped_file_sz);
|
||||||
@ -438,15 +441,19 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
|
|||||||
if (tt == 'd') {
|
if (tt == 'd') {
|
||||||
if (img->load_data.buf_used < img->load_data.data_sz) {
|
if (img->load_data.buf_used < img->load_data.data_sz) {
|
||||||
ABRT(ENODATA, "Insufficient image data: %zu < %zu", img->load_data.buf_used, img->load_data.data_sz);
|
ABRT(ENODATA, "Insufficient image data: %zu < %zu", img->load_data.buf_used, img->load_data.data_sz);
|
||||||
img->data_loaded = false;
|
|
||||||
} else img->load_data.data = img->load_data.buf;
|
} else img->load_data.data = img->load_data.buf;
|
||||||
} else {
|
} else {
|
||||||
if (img->load_data.mapped_file_sz < img->load_data.data_sz) {
|
if (img->load_data.mapped_file_sz < img->load_data.data_sz) {
|
||||||
ABRT(ENODATA, "Insufficient image data: %zu < %zu", img->load_data.mapped_file_sz, img->load_data.data_sz);
|
ABRT(ENODATA, "Insufficient image data: %zu < %zu", img->load_data.mapped_file_sz, img->load_data.data_sz);
|
||||||
img->data_loaded = false;
|
|
||||||
} else img->load_data.data = img->load_data.mapped_file;
|
} else img->load_data.data = img->load_data.mapped_file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
size_t required_sz = (img->load_data.is_rgb ? 3 : 4) * img->width * img->height;
|
||||||
|
if (img->load_data.data_sz != required_sz) ABRT(EINVAL, "Image dimensions: %ux%u do not match data size: %zu, expected size: %zu", img->width, img->height, img->load_data.data_sz, required_sz);
|
||||||
|
if (LIKELY(img->data_loaded && send_to_gpu)) {
|
||||||
|
send_image_to_gpu(&img->texture_id, img->load_data.data, img->width, img->height, img->load_data.is_rgb, img->load_data.is_4byte_aligned);
|
||||||
|
free_load_data(&img->load_data);
|
||||||
|
}
|
||||||
return img;
|
return img;
|
||||||
#undef MAX_DATA_SZ
|
#undef MAX_DATA_SZ
|
||||||
#undef ABRT
|
#undef ABRT
|
||||||
@ -470,16 +477,20 @@ create_add_response(GraphicsManager UNUSED *self, const GraphicsCommand *g, bool
|
|||||||
|
|
||||||
// Displaying images {{{
|
// Displaying images {{{
|
||||||
|
|
||||||
|
#define ensure_space_for(base, array, type, num, capacity, initial_cap) \
|
||||||
|
if (base->capacity < num) { \
|
||||||
|
base->capacity = MAX(initial_cap, MAX(2 * base->capacity, num)); \
|
||||||
|
base->array = realloc(base->array, sizeof(type) * base->capacity); \
|
||||||
|
if (base->array == NULL) fatal("Out of memory while ensuring space in array"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, bool *is_dirty, Image *img) {
|
handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, bool *is_dirty, Image *img) {
|
||||||
if (img == NULL) img = img_by_client_id(self, g->id);
|
if (img == NULL) img = img_by_client_id(self, g->id);
|
||||||
if (img == NULL) { REPORT_ERROR("Put command refers to non-existent image with id: %u", g->id); return; }
|
if (img == NULL) { REPORT_ERROR("Put command refers to non-existent image with id: %u", g->id); return; }
|
||||||
if (!img->data_loaded) { REPORT_ERROR("Put command refers to image with id: %u that could not load its data", g->id); return; }
|
if (!img->data_loaded) { REPORT_ERROR("Put command refers to image with id: %u that could not load its data", g->id); return; }
|
||||||
if (img->refcnt >= img->refcap) {
|
ensure_space_for(img, refs, ImageRef, img->refcnt + 1, refcap, 10);
|
||||||
img->refcap = MAX(10, img->refcap * 2);
|
|
||||||
img->refs = realloc(img->refs, img->refcap * sizeof(ImageRef));
|
|
||||||
if (img->refs == NULL) { REPORT_ERROR("Out of memory growing image refs array"); img->refcap = 0; return; }
|
|
||||||
}
|
|
||||||
*is_dirty = true;
|
*is_dirty = true;
|
||||||
self->layers_dirty = true;
|
self->layers_dirty = true;
|
||||||
ImageRef *ref = NULL;
|
ImageRef *ref = NULL;
|
||||||
@ -519,13 +530,6 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b
|
|||||||
c->x += num_cols; c->y += num_rows;
|
c->x += num_cols; c->y += num_rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ensure_space_for(base, array, type, num, capacity) \
|
|
||||||
if (base->capacity < num) { \
|
|
||||||
base->capacity = MAX(100, MAX(2 * base->capacity, num)); \
|
|
||||||
base->array = realloc(base->array, sizeof(type) * base->capacity); \
|
|
||||||
if (base->array == NULL) fatal("Out of memory while ensuring space in array"); \
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cmp_by_zindex_and_image(const void *a_, const void *b_) {
|
cmp_by_zindex_and_image(const void *a_, const void *b_) {
|
||||||
const ImageRenderData *a = (const ImageRenderData*)a_, *b = (const ImageRenderData*)b_;
|
const ImageRenderData *a = (const ImageRenderData*)a_, *b = (const ImageRenderData*)b_;
|
||||||
@ -549,7 +553,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by) {
|
|||||||
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; for (j = 0; j < img->refcnt; j++) { ref = img->refs + j;
|
||||||
/* TODO: calculate geometry and ignore refs outside of current viewport */
|
/* TODO: calculate geometry and ignore refs outside of current viewport */
|
||||||
if (ref->z_index < 0) self->num_of_negative_refs++; else self->num_of_positive_refs++;
|
if (ref->z_index < 0) self->num_of_negative_refs++; else self->num_of_positive_refs++;
|
||||||
ensure_space_for(self, render_data, ImageRenderData, self->count + 1, capacity);
|
ensure_space_for(self, render_data, ImageRenderData, self->count + 1, capacity, 100);
|
||||||
ImageRenderData *rd = self->render_data + self->count;
|
ImageRenderData *rd = self->render_data + self->count;
|
||||||
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;
|
||||||
@ -557,7 +561,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by) {
|
|||||||
}}
|
}}
|
||||||
if (!self->count) return;
|
if (!self->count) return;
|
||||||
// Sort visible refs in draw order (z-index, img)
|
// Sort visible refs in draw order (z-index, img)
|
||||||
ensure_space_for(self, render_pointers, ImageRenderData*, self->count, rp_capacity);
|
ensure_space_for(self, render_pointers, ImageRenderData*, self->count, rp_capacity, 100);
|
||||||
for (i = 0; i < self->count; i++) self->render_pointers[i] = self->render_data + i;
|
for (i = 0; i < self->count; i++) self->render_pointers[i] = self->render_data + i;
|
||||||
qsort(self->render_pointers, self->count, sizeof(ImageRenderData*), cmp_by_zindex_and_image);
|
qsort(self->render_pointers, self->count, sizeof(ImageRenderData*), cmp_by_zindex_and_image);
|
||||||
}
|
}
|
||||||
@ -654,12 +658,15 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
#define M(x, va) {#x, (PyCFunction)py##x, va, ""}
|
#define M(x, va) {#x, (PyCFunction)py##x, va, ""}
|
||||||
|
|
||||||
static PyMethodDef methods[] = {
|
static PyMethodDef methods[] = {
|
||||||
M(image_for_client_id, METH_O),
|
M(image_for_client_id, METH_O),
|
||||||
M(shm_write, METH_VARARGS),
|
|
||||||
M(shm_unlink, METH_VARARGS),
|
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -675,10 +682,19 @@ PyTypeObject GraphicsManager_Type = {
|
|||||||
.tp_methods = methods,
|
.tp_methods = methods,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PyMethodDef module_methods[] = {
|
||||||
|
M(shm_write, METH_VARARGS),
|
||||||
|
M(shm_unlink, METH_VARARGS),
|
||||||
|
M(set_send_to_gpu, METH_O),
|
||||||
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
init_graphics(PyObject *module) {
|
init_graphics(PyObject *module) {
|
||||||
if (PyType_Ready(&GraphicsManager_Type) < 0) return false;
|
if (PyType_Ready(&GraphicsManager_Type) < 0) return false;
|
||||||
if (PyModule_AddObject(module, "GraphicsManager", (PyObject *)&GraphicsManager_Type) != 0) return false;
|
if (PyModule_AddObject(module, "GraphicsManager", (PyObject *)&GraphicsManager_Type) != 0) return false;
|
||||||
|
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||||
Py_INCREF(&GraphicsManager_Type);
|
Py_INCREF(&GraphicsManager_Type);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@ typedef struct {
|
|||||||
size_t data_sz;
|
size_t data_sz;
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
bool is_4byte_aligned;
|
bool is_4byte_aligned;
|
||||||
|
bool is_rgb;
|
||||||
} LoadData;
|
} LoadData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@ -561,6 +561,7 @@ init_shaders(PyObject *module) {
|
|||||||
draw_cells = &draw_cells_impl;
|
draw_cells = &draw_cells_impl;
|
||||||
draw_cursor = &draw_cursor_impl;
|
draw_cursor = &draw_cursor_impl;
|
||||||
free_texture = &free_texture_impl;
|
free_texture = &free_texture_impl;
|
||||||
|
send_image_to_gpu = &send_image_to_gpu_impl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
|||||||
@ -102,3 +102,4 @@ EXTERNAL_FUNC(draw_cells, void, ssize_t, float, float, float, float, Screen *, C
|
|||||||
EXTERNAL_FUNC(draw_cursor, void, CursorRenderInfo *);
|
EXTERNAL_FUNC(draw_cursor, void, CursorRenderInfo *);
|
||||||
EXTERNAL_FUNC(update_viewport_size, void, int, int);
|
EXTERNAL_FUNC(update_viewport_size, void, int, int);
|
||||||
EXTERNAL_FUNC(free_texture, void, uint32_t*);
|
EXTERNAL_FUNC(free_texture, void, uint32_t*);
|
||||||
|
EXTERNAL_FUNC(send_image_to_gpu, void, uint32_t*, const void*, int32_t width, int32_t height, bool is_rgba, bool is_4byte_aligned);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import zlib
|
|||||||
from base64 import standard_b64encode
|
from base64 import standard_b64encode
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from kitty.fast_data_types import parse_bytes
|
from kitty.fast_data_types import parse_bytes, shm_write, shm_unlink, set_send_to_gpu
|
||||||
|
|
||||||
from . import BaseTest
|
from . import BaseTest
|
||||||
|
|
||||||
@ -18,6 +18,8 @@ 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__))
|
||||||
@ -110,10 +112,10 @@ class TestGraphics(BaseTest):
|
|||||||
|
|
||||||
# Test loading from POSIX SHM
|
# Test loading from POSIX SHM
|
||||||
name = '/kitty-test-shm'
|
name = '/kitty-test-shm'
|
||||||
g.shm_write(name, random_data)
|
shm_write(name, random_data)
|
||||||
sl(name, s=24, v=32, t='s', expecting_data=random_data)
|
sl(name, s=24, v=32, t='s', expecting_data=random_data)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
FileNotFoundError, g.shm_unlink, name
|
FileNotFoundError, shm_unlink, name
|
||||||
) # check that file was deleted
|
) # check that file was deleted
|
||||||
|
|
||||||
@unittest.skipIf(Image is None, 'PIL not available, skipping PNG tests')
|
@unittest.skipIf(Image is None, 'PIL not available, skipping PNG tests')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user