Implement sending of image data to GPU

This commit is contained in:
Kovid Goyal 2017-10-01 09:04:46 +05:30
parent e2a8115328
commit 3cbc20005b
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 59 additions and 25 deletions

View File

@ -97,6 +97,19 @@ free_texture_impl(GLuint *tex_id) {
*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 {{{

View File

@ -19,6 +19,8 @@
#define REPORT_ERROR(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
static bool send_to_gpu = true;
GraphicsManager*
grman_realloc(GraphicsManager *old, index_type lines, index_type columns) {
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;
send_image_to_gpu_func send_image_to_gpu = NULL;
static inline void
free_image(Image *img) {
@ -306,11 +309,11 @@ remove_images(GraphicsManager *self, bool(*predicate)(Image*)) {
static Image*
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)
has_add_respose = false;
bool existing, init_img = true;
Image *img;
Image *img = NULL;
unsigned char tt = g->transmission_type ? g->transmission_type : 'd';
enum FORMATS { RGB=24, RGBA=32, PNG=100 };
uint32_t fmt = g->format ? g->format : RGBA;
@ -335,6 +338,7 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
case PNG:
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_rgb = false;
img->load_data.data_sz = g->data_sz ? g->data_sz : 1024 * 100;
break;
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);
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_rgb = fmt == RGB;
break;
default:
ABRT(EINVAL, "Unknown image format: %u", fmt);
@ -413,7 +418,6 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
break;
default:
ABRT(EINVAL, "Unknown image compression: %c", g->compressed);
img->data_loaded = false; return NULL;
}
switch(fmt) {
case PNG:
@ -428,7 +432,6 @@ handle_add_command(GraphicsManager *self, const GraphicsCommand *g, const uint8_
img->load_data.data = img->load_data.buf;
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);
img->data_loaded = false;
}
if (img->load_data.mapped_file) {
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 (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 {
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);
img->data_loaded = false;
} 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;
#undef MAX_DATA_SZ
#undef ABRT
@ -470,16 +477,20 @@ create_add_response(GraphicsManager UNUSED *self, const GraphicsCommand *g, bool
// 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
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) { 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->refcnt >= img->refcap) {
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; }
}
ensure_space_for(img, refs, ImageRef, img->refcnt + 1, refcap, 10);
*is_dirty = true;
self->layers_dirty = true;
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;
}
#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
cmp_by_zindex_and_image(const void *a_, const void *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;
/* 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++;
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;
self->count++;
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;
// 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;
qsort(self->render_pointers, self->count, sizeof(ImageRenderData*), cmp_by_zindex_and_image);
}
@ -654,12 +658,15 @@ W(shm_unlink) {
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, ""}
static PyMethodDef methods[] = {
M(image_for_client_id, METH_O),
M(shm_write, METH_VARARGS),
M(shm_unlink, METH_VARARGS),
{NULL} /* Sentinel */
};
@ -675,10 +682,19 @@ PyTypeObject GraphicsManager_Type = {
.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
init_graphics(PyObject *module) {
if (PyType_Ready(&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);
return true;
}

View File

@ -25,6 +25,7 @@ typedef struct {
size_t data_sz;
uint8_t *data;
bool is_4byte_aligned;
bool is_rgb;
} LoadData;
typedef struct {

View File

@ -561,6 +561,7 @@ init_shaders(PyObject *module) {
draw_cells = &draw_cells_impl;
draw_cursor = &draw_cursor_impl;
free_texture = &free_texture_impl;
send_image_to_gpu = &send_image_to_gpu_impl;
return true;
}
// }}}

View File

@ -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(update_viewport_size, void, int, int);
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);

View File

@ -9,7 +9,7 @@ import zlib
from base64 import standard_b64encode
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
@ -18,6 +18,8 @@ try:
except ImportError:
Image = None
set_send_to_gpu(False)
def relpath(name):
base = os.path.dirname(os.path.abspath(__file__))
@ -110,10 +112,10 @@ class TestGraphics(BaseTest):
# Test loading from POSIX 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)
self.assertRaises(
FileNotFoundError, g.shm_unlink, name
FileNotFoundError, shm_unlink, name
) # check that file was deleted
@unittest.skipIf(Image is None, 'PIL not available, skipping PNG tests')