From 248301f8b3be5325975c345dafe2a8f84f8f6359 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 22 Jun 2023 19:26:36 +0530 Subject: [PATCH] Cleanup a bunch of shader infrastructure 1) No longer us glScissor. It's an awful API and is not available in Vulkan. Instead the graphics drawing code ensures the graphic is drawn within the current viewport 2) Use generated code to automatically get the locations of uniforms from shaders. Greatly simplifies adding new uniforms to a shader. 3) Dont use a VAO for loading graphics vertices. Greatly simplifies a bunch of book keeping code. --- kitty/child-monitor.c | 8 +- kitty/gl.c | 11 +- kitty/graphics.c | 34 ++-- kitty/graphics.h | 2 +- kitty/graphics_fragment.glsl | 1 - kitty/graphics_vertex.glsl | 22 ++- kitty/shaders.c | 331 +++++++++++++++++------------------ kitty/state.c | 7 - kitty/state.h | 7 +- setup.py | 52 +++++- 10 files changed, 254 insertions(+), 221 deletions(-) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 7a2332390..9f3821a3c 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -675,7 +675,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * call_boss(update_tab_bar_data, "K", os_window->id); os_window->tab_bar_data_updated = true; } - if (send_cell_data_to_gpu(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window)) needs_render = true; + if (send_cell_data_to_gpu(TD.vao_idx, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, os_window)) needs_render = true; } if (OPT(mouse_hide_wait) > 0 && !is_mouse_hidden(os_window)) { if (now - os_window->last_mouse_activity_at >= OPT(mouse_hide_wait)) hide_mouse(os_window); @@ -726,7 +726,7 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * set_maximum_wait(min_gap); } } - if (send_cell_data_to_gpu(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) needs_render = true; + if (send_cell_data_to_gpu(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, os_window)) needs_render = true; if (WD.screen->start_visual_bell_at != 0) needs_render = true; } } @@ -753,12 +753,12 @@ render_os_window(OSWindow *os_window, unsigned int active_window_id, color_type BorderRects *br = &tab->border_rects; draw_borders(br->vao_idx, br->num_border_rects, br->rect_buf, br->is_dirty, os_window->viewport_width, os_window->viewport_height, active_window_bg, num_visible_windows, all_windows_have_same_bg, os_window); br->is_dirty = false; - if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(TD.vao_idx, 0, &TD, os_window, true, false, NULL); + if (TD.screen && os_window->num_tabs >= OPT(tab_bar_min_tabs)) draw_cells(TD.vao_idx, &TD, os_window, true, false, NULL); for (unsigned int i = 0; i < tab->num_windows; i++) { Window *w = tab->windows + i; if (w->visible && WD.screen) { bool is_active_window = i == tab->active_window; - draw_cells(WD.vao_idx, WD.gvao_idx, &WD, os_window, is_active_window, true, w); + draw_cells(WD.vao_idx, &WD, os_window, is_active_window, true, w); if (WD.screen->start_visual_bell_at != 0) { set_maximum_wait(OPT(repaint_delay)); } diff --git a/kitty/gl.c b/kitty/gl.c index c8874d797..188f4562f 100644 --- a/kitty/gl.c +++ b/kitty/gl.c @@ -132,6 +132,8 @@ init_uniforms(int program) { for (GLint i = 0; i < p->num_of_uniforms; i++) { Uniform *u = p->uniforms + i; glGetActiveUniform(p->id, (GLuint)i, sizeof(u->name)/sizeof(u->name[0]), NULL, &(u->size), &(u->type), u->name); + char *l = strchr(u->name, '['); + if (l) *l = 0; u->location = glGetUniformLocation(p->id, u->name); u->idx = i; } @@ -140,13 +142,18 @@ init_uniforms(int program) { GLint get_uniform_location(int program, const char *name) { Program *p = programs + program; - return glGetUniformLocation(p->id, name); + const size_t n = strlen(name) + 1; + for (GLint i = 0; i < p->num_of_uniforms; i++) { + Uniform *u = p->uniforms + i; + if (strncmp(u->name, name, n) == 0) return u->location; + } + return -1; } GLint get_uniform_information(int program, const char *name, GLenum information_type) { GLint q; GLuint t; - static const char* names[] = {""}; + const char* names[] = {""}; names[0] = name; GLuint pid = program_id(program); glGetUniformIndices(pid, 1, (void*)names, &t); diff --git a/kitty/graphics.c b/kitty/graphics.c index dbfa0ae4a..7aac385eb 100644 --- a/kitty/graphics.c +++ b/kitty/graphics.c @@ -899,35 +899,21 @@ handle_put_command(GraphicsManager *self, const GraphicsCommand *g, Cursor *c, b return img->client_id; } -static void -set_vertex_data(ImageRenderData *rd, const ImageRef *ref, const ImageRect *dest_rect) { - unsigned n = 0; -#define R(a, b) rd->vertices[n*4] = ref->src_rect.a; rd->vertices[n*4 + 1] = ref->src_rect.b; rd->vertices[n*4 + 2] = dest_rect->a; rd->vertices[n*4 + 3] = dest_rect->b; n++; - R(right, top); R(right, bottom); R(left, bottom); R(left, top); -#undef R -} - void scale_rendered_graphic(ImageRenderData *rd, float xstart, float ystart, float x_scale, float y_scale) { - float right = rd->vertices[2], left = rd->vertices[8 + 2], top = rd->vertices[3], bottom = rd->vertices[4 + 3]; - float width = right - left, height = bottom - top; - left = xstart + (left - xstart) * x_scale; - right = left + width * x_scale; - top = ystart + (top - ystart) * y_scale; - bottom = top + height * y_scale; - rd->vertices[0+2] = right; rd->vertices[4+2] = right; - rd->vertices[8+2] = left; rd->vertices[12+2] = left; - rd->vertices[0+3] = top; rd->vertices[12+3] = top; - rd->vertices[4+3] = bottom; rd->vertices[8+3] = bottom; + float width = rd->dest_rect.right - rd->dest_rect.left, height = rd->dest_rect.bottom - rd->dest_rect.top; + rd->dest_rect.left = xstart + (rd->dest_rect.left - xstart) * x_scale; + rd->dest_rect.right = rd->dest_rect.left + width * x_scale; + rd->dest_rect.top = ystart + (rd->dest_rect.top - ystart) * y_scale; + rd->dest_rect.bottom = rd->dest_rect.top + height * y_scale; } void gpu_data_for_image(ImageRenderData *ans, float left, float top, float right, float bottom) { // For dest rect: x-axis is from -1 to 1, y axis is from 1 to -1 static const ImageRef source_rect = { .src_rect = { .left=0, .top=0, .bottom=1, .right=1 }}; - const ImageRef *ref = &source_rect; - const ImageRect r = { .left = left, .right = right, .top = top, .bottom = bottom }; - set_vertex_data(ans, ref, &r); + ans->src_rect = source_rect.src_rect; + ans->dest_rect = (ImageRect){ .left = left, .right = right, .top = top, .bottom = bottom }; ans->group_count = 1; } @@ -976,7 +962,7 @@ grman_update_layers(GraphicsManager *self, unsigned int scrolled_by, float scree ensure_space_for(self, render_data, ImageRenderData, self->count + 1, capacity, 64, true); ImageRenderData *rd = self->render_data + self->count; zero_at_ptr(rd); - set_vertex_data(rd, ref, &r); + rd->dest_rect = r; rd->src_rect = ref->src_rect; self->count++; rd->z_index = ref->z_index; rd->image_id = img->internal_id; rd->texture_id = img->texture_id; @@ -2026,9 +2012,9 @@ W(update_layers) { PyObject *ans = PyTuple_New(self->count); for (size_t i = 0; i < self->count; i++) { ImageRenderData *r = self->render_data + i; -#define R(offset) Py_BuildValue("{sf sf sf sf}", "left", r->vertices[offset + 8], "top", r->vertices[offset + 1], "right", r->vertices[offset], "bottom", r->vertices[offset + 5]) +#define R(which) Py_BuildValue("{sf sf sf sf}", "left", r->which.left, "top", r->which.top, "right", r->which.right, "bottom", r->which.bottom) PyTuple_SET_ITEM(ans, i, - Py_BuildValue("{sN sN sI si sK}", "src_rect", R(0), "dest_rect", R(2), "group_count", r->group_count, "z_index", r->z_index, "image_id", r->image_id) + Py_BuildValue("{sN sN sI si sK}", "src_rect", R(src_rect), "dest_rect", R(dest_rect), "group_count", r->group_count, "z_index", r->z_index, "image_id", r->image_id) ); #undef R } diff --git a/kitty/graphics.h b/kitty/graphics.h index 845c94156..2e4ca6a36 100644 --- a/kitty/graphics.h +++ b/kitty/graphics.h @@ -76,7 +76,7 @@ typedef struct { } BackgroundImage; typedef struct { - float vertices[16]; + ImageRect src_rect, dest_rect; uint32_t texture_id, group_count; int z_index; id_type image_id; diff --git a/kitty/graphics_fragment.glsl b/kitty/graphics_fragment.glsl index d4d1cd94f..95ce0cbbb 100644 --- a/kitty/graphics_fragment.glsl +++ b/kitty/graphics_fragment.glsl @@ -1,4 +1,3 @@ -#extension GL_ARB_explicit_attrib_location : require #pragma kitty_include_shader #define ALPHA_TYPE diff --git a/kitty/graphics_vertex.glsl b/kitty/graphics_vertex.glsl index 3319f841a..ec782a3ec 100644 --- a/kitty/graphics_vertex.glsl +++ b/kitty/graphics_vertex.glsl @@ -1,10 +1,20 @@ -#extension GL_ARB_explicit_attrib_location : require - -// Have to use fixed locations here as all variants of the program share the same VAO -layout(location=0) in vec4 src; out vec2 texcoord; +uniform vec4 src_rect, dest_rect; + +#define left 0 +#define top 1 +#define right 2 +#define bottom 3 + +const ivec2 vertex_pos_map[4] = ivec2[4]( + ivec2(right, top), + ivec2(right, bottom), + ivec2(left, bottom), + ivec2(left, top) +); void main() { - texcoord = vec2(src[0], src[1]); - gl_Position = vec4(src[2], src[3], 0, 1); + ivec2 pos = vertex_pos_map[gl_VertexID]; + texcoord = vec2(src_rect[pos.x], src_rect[pos.y]); + gl_Position = vec4(dest_rect[pos.x], dest_rect[pos.y], 0, 1); } diff --git a/kitty/shaders.c b/kitty/shaders.c index d312da9dd..686922765 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -11,6 +11,7 @@ #include #include "window_logo.h" #include "srgb_gamma.h" +#include "uniforms_generated.h" #define BLEND_ONTO_OPAQUE glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // blending onto opaque colors #define BLEND_ONTO_OPAQUE_WITH_OPAQUE_OUTPUT glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); // blending onto opaque colors with final color having alpha 1 @@ -187,28 +188,30 @@ typedef struct CellRenderData { struct { GLfloat xstart, ystart, dx, dy, width, height; } gl; - struct { - GLint xstart, ystart; - GLsizei width, height; - } px; float x_ratio, y_ratio; } CellRenderData; typedef struct { UniformBlock render_data; ArrayInformation color_table; - GLint draw_bg_bitfield_location; + CellUniforms uniforms; } CellProgramLayout; - static CellProgramLayout cell_program_layouts[NUM_PROGRAMS]; + typedef struct { - GLint image_location, tiled_location, sizes_location, positions_location, opacity_location, premult_location; + GraphicsUniforms uniforms; +} GraphicsProgramLayout; +static GraphicsProgramLayout graphics_program_layouts[NUM_PROGRAMS]; + +typedef struct { + BgimageUniforms uniforms; } BGImageProgramLayout; -static BGImageProgramLayout bgimage_program_layout = {0}; +static BGImageProgramLayout bgimage_program_layout; + typedef struct { - GLint tint_color_location, edges_location; + TintUniforms uniforms; } TintProgramLayout; -static TintProgramLayout tint_program_layout = {0}; +static TintProgramLayout tint_program_layout; static void init_cell_program(void) { @@ -218,22 +221,20 @@ init_cell_program(void) { cell_program_layouts[i].color_table.size = get_uniform_information(i, "color_table[0]", GL_UNIFORM_SIZE); cell_program_layouts[i].color_table.offset = get_uniform_information(i, "color_table[0]", GL_UNIFORM_OFFSET); cell_program_layouts[i].color_table.stride = get_uniform_information(i, "color_table[0]", GL_UNIFORM_ARRAY_STRIDE); + get_uniform_locations_cell(i, &cell_program_layouts[i].uniforms); } - cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location = get_uniform_location(CELL_BG_PROGRAM, "draw_bg_bitfield"); + // Sanity check to ensure the attribute location binding worked #define C(p, name, expected) { int aloc = attrib_location(p, #name); if (aloc != expected && aloc != -1) fatal("The attribute location for %s is %d != %d in program: %d", #name, aloc, expected, p); } for (int p = CELL_PROGRAM; p < BORDERS_PROGRAM; p++) { C(p, colors, 0); C(p, sprite_coords, 1); C(p, is_selected, 2); } #undef C - bgimage_program_layout.image_location = get_uniform_location(BGIMAGE_PROGRAM, "image"); - bgimage_program_layout.opacity_location = get_uniform_location(BGIMAGE_PROGRAM, "opacity"); - bgimage_program_layout.sizes_location = get_uniform_location(BGIMAGE_PROGRAM, "sizes"); - bgimage_program_layout.positions_location = get_uniform_location(BGIMAGE_PROGRAM, "positions"); - bgimage_program_layout.tiled_location = get_uniform_location(BGIMAGE_PROGRAM, "tiled"); - bgimage_program_layout.premult_location = get_uniform_location(BGIMAGE_PROGRAM, "premult"); - tint_program_layout.tint_color_location = get_uniform_location(TINT_PROGRAM, "tint_color"); - tint_program_layout.edges_location = get_uniform_location(TINT_PROGRAM, "edges"); + for (int i = GRAPHICS_PROGRAM; i <= GRAPHICS_ALPHA_MASK_PROGRAM; i++) { + get_uniform_locations_graphics(i, &graphics_program_layouts[i].uniforms); + } + get_uniform_locations_bgimage(BGIMAGE_PROGRAM, &bgimage_program_layout.uniforms); + get_uniform_locations_tint(TINT_PROGRAM, &tint_program_layout.uniforms); } #define CELL_BUFFERS enum { cell_data_buffer, selection_buffer, uniform_buffer }; @@ -269,23 +270,6 @@ create_graphics_vao(void) { return vao_idx; } -struct CellUniformData { - bool constants_set; - GLint gploc, gpploc, cploc, cfploc, fg_loc, amask_bg_premult_loc, amask_fg_loc, amask_image_loc; - GLfloat prev_inactive_text_alpha; -}; - -static struct CellUniformData cell_uniform_data = {0, .prev_inactive_text_alpha=-1}; - -static void -send_graphics_data_to_gpu(size_t image_count, ssize_t gvao_idx, const ImageRenderData *render_data) { - const size_t num_of_floats_per_image = arraysz(render_data[0].vertices); - size_t sz = sizeof(GLfloat) * num_of_floats_per_image * image_count; - GLfloat *a = alloc_and_map_vao_buffer(gvao_idx, sz, 0, GL_STREAM_DRAW, GL_WRITE_ONLY); - for (size_t i = 0; i < image_count; i++, a += num_of_floats_per_image) memcpy(a, render_data[i].vertices, sizeof(render_data[0].vertices)); - unmap_vao_buffer(gvao_idx, 0); a = NULL; -} - #define IS_SPECIAL_COLOR(name) (screen->color_profile->overridden.name.type == COLOR_IS_SPECIAL || (screen->color_profile->overridden.name.type == COLOR_NOT_SET && screen->color_profile->configured.name.type == COLOR_IS_SPECIAL)) static void @@ -388,7 +372,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, c } static bool -cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, FONTS_DATA_HANDLE fonts_data) { +cell_prepare_to_render(ssize_t vao_idx, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, FONTS_DATA_HANDLE fonts_data) { size_t sz; CELL_BUFFERS; void *address; @@ -422,8 +406,7 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa changed = true; } - if (gvao_idx && grman_update_layers(screen->grman, screen->scrolled_by, xstart, ystart, dx, dy, screen->columns, screen->lines, screen->cell_size)) { - send_graphics_data_to_gpu(screen->grman->count, gvao_idx, screen->grman->render_data); + if (grman_update_layers(screen->grman, screen->scrolled_by, xstart, ystart, dx, dy, screen->columns, screen->lines, screen->cell_size)) { changed = true; } screen->last_rendered.scrolled_by = screen->scrolled_by; @@ -437,16 +420,16 @@ draw_background_image(OSWindow *w) { blank_canvas(w->is_semi_transparent ? OPT(background_opacity) : 1.0f, OPT(background)); bind_program(BGIMAGE_PROGRAM); - glUniform1i(bgimage_program_layout.image_location, BGIMAGE_UNIT); - glUniform1f(bgimage_program_layout.opacity_location, OPT(background_opacity)); + glUniform1i(bgimage_program_layout.uniforms.image, BGIMAGE_UNIT); + glUniform1f(bgimage_program_layout.uniforms.opacity, OPT(background_opacity)); #ifdef __APPLE__ int window_width = w->window_width, window_height = w->window_height; #else int window_width = w->viewport_width, window_height = w->viewport_height; #endif - glUniform4f(bgimage_program_layout.sizes_location, + glUniform4f(bgimage_program_layout.uniforms.sizes, (GLfloat)window_width, (GLfloat)window_height, (GLfloat)w->bgimage->width, (GLfloat)w->bgimage->height); - glUniform1f(bgimage_program_layout.premult_location, w->is_semi_transparent ? 1.f : 0.f); + glUniform1f(bgimage_program_layout.uniforms.premult, w->is_semi_transparent ? 1.f : 0.f); GLfloat tiled = 0.f;; GLfloat left = -1.0, top = 1.0, right = 1.0, bottom = -1.0; switch (OPT(background_image_layout)) { @@ -466,8 +449,8 @@ draw_background_image(OSWindow *w) { } break; } - glUniform1f(bgimage_program_layout.tiled_location, tiled); - glUniform4f(bgimage_program_layout.positions_location, left, top, right, bottom); + glUniform1f(bgimage_program_layout.uniforms.tiled, tiled); + glUniform4f(bgimage_program_layout.uniforms.positions, left, top, right, bottom); glActiveTexture(GL_TEXTURE0 + BGIMAGE_UNIT); glBindTexture(GL_TEXTURE_2D, w->bgimage->texture_id); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); @@ -475,22 +458,48 @@ draw_background_image(OSWindow *w) { } static void -draw_graphics(int program, ssize_t vao_idx, ssize_t gvao_idx, ImageRenderData *data, GLuint start, GLuint count) { - bind_program(program); - bind_vertex_array(gvao_idx); - glActiveTexture(GL_TEXTURE0 + GRAPHICS_UNIT); +fit_image_in_viewport(GraphicsUniforms *u, ImageRenderData *rd, ImageRect viewport) { + // dest_rect: x: -1 to 1, y: 1 to -1. src_rect: x: 0 to 1 for both axes + ImageRect src_rect = rd->src_rect, dest_rect = rd->dest_rect; + const float img_width = dest_rect.right - dest_rect.left; + if (viewport.left > dest_rect.left) { + float frac = 1.f - (dest_rect.right - viewport.left) / img_width; + dest_rect.left = viewport.left; + src_rect.left += frac; // src_rect: left is 0 right is 1 + } + if (viewport.right < dest_rect.right) { + float frac = 1.f - (viewport.right - dest_rect.left) / img_width; + dest_rect.right = viewport.right; + src_rect.right -= frac; // src_rect: left is 0 right is 1 + } + const float img_height = dest_rect.top - dest_rect.bottom; + if (viewport.top < dest_rect.top) { + float frac = 1.f - (viewport.top - dest_rect.bottom) / img_height; + dest_rect.top = viewport.top; + src_rect.top += frac; // src_rect.bottom is 1 top is 0 + } + if (viewport.bottom > dest_rect.bottom) { + float frac = 1.f - (dest_rect.top - viewport.bottom) / img_height; + dest_rect.bottom = viewport.bottom; + src_rect.bottom -= frac; // src_rect.bottom is 1 top is 0 + } + glUniform4f(u->src_rect, src_rect.left, src_rect.top, src_rect.right, src_rect.bottom); + glUniform4f(u->dest_rect, dest_rect.left, dest_rect.top, dest_rect.right, dest_rect.bottom); +} - GLuint base = 4 * start; - glEnable(GL_SCISSOR_TEST); +static void +draw_graphics(int program, ssize_t vao_idx, ImageRenderData *data, GLuint start, GLuint count, ImageRect viewport) { + bind_program(program); + glActiveTexture(GL_TEXTURE0 + GRAPHICS_UNIT); + GraphicsUniforms *u = &graphics_program_layouts[program].uniforms; for (GLuint i=0; i < count;) { ImageRenderData *rd = data + start + i; glBindTexture(GL_TEXTURE_2D, rd->texture_id); - // You could reduce the number of draw calls by using - // glDrawArraysInstancedBaseInstance but Apple chose to abandon OpenGL - // before implementing it. - for (GLuint k=0; k < rd->group_count; k++, base += 4, i++) glDrawArrays(GL_TRIANGLE_FAN, base, 4); + for (GLuint k=0; k < rd->group_count; k++, i++) { + fit_image_in_viewport(u, rd, viewport); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + } } - glDisable(GL_SCISSOR_TEST); bind_vertex_array(vao_idx); } @@ -522,29 +531,32 @@ draw_centered_alpha_mask(OSWindow *os_window, size_t screen_width, size_t screen ImageRenderData *data = load_alpha_mask_texture(width, height, canvas); gpu_data_for_centered_image(data, screen_width, screen_height, width, height); bind_program(GRAPHICS_ALPHA_MASK_PROGRAM); - glUniform1i(cell_uniform_data.amask_image_loc, GRAPHICS_UNIT); - color_vec3(cell_uniform_data.amask_fg_loc, OPT(foreground)); - color_vec4_premult(cell_uniform_data.amask_bg_premult_loc, OPT(background), 1); - send_graphics_data_to_gpu(1, os_window->gvao_idx, data); + glUniform1i(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.image, GRAPHICS_UNIT); + color_vec3(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.amask_fg, OPT(foreground)); + color_vec4_premult(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.amask_bg_premult, OPT(background), 1); glEnable(GL_BLEND); if (os_window->is_semi_transparent) { BLEND_PREMULT; } else { BLEND_ONTO_OPAQUE; } - glScissor(0, 0, screen_width, screen_height); - draw_graphics(GRAPHICS_ALPHA_MASK_PROGRAM, 0, os_window->gvao_idx, data, 0, 1); + draw_graphics(GRAPHICS_ALPHA_MASK_PROGRAM, 0, data, 0, 1, (ImageRect){-1, 1, 1, -1}); glDisable(GL_BLEND); } +static ImageRect +viewport_for_cells(const CellRenderData *crd) { + return (ImageRect){crd->gl.xstart, crd->gl.ystart, crd->gl.xstart + crd->gl.width, crd->gl.ystart - crd->gl.height}; +} + static void -draw_cells_simple(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) { +draw_cells_simple(ssize_t vao_idx, Screen *screen, const CellRenderData *crd) { bind_program(CELL_PROGRAM); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); if (screen->grman->count) { glEnable(GL_BLEND); BLEND_ONTO_OPAQUE; - draw_graphics(GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->count); + draw_graphics(GRAPHICS_PROGRAM, vao_idx, screen->grman->render_data, 0, screen->grman->count, viewport_for_cells(crd)); glDisable(GL_BLEND); } } @@ -561,43 +573,45 @@ draw_tint(bool premult, Screen *screen, const CellRenderData *crd) { color_type window_bg = colorprofile_to_color(screen->color_profile, screen->color_profile->overridden.default_bg, screen->color_profile->configured.default_bg).rgb; #define C(shift) srgb_color((window_bg >> shift) & 0xFF) * premult_factor GLfloat premult_factor = premult ? OPT(background_tint) : 1.0f; - glUniform4f(tint_program_layout.tint_color_location, C(16), C(8), C(0), OPT(background_tint)); + glUniform4f(tint_program_layout.uniforms.tint_color, C(16), C(8), C(0), OPT(background_tint)); #undef C - glUniform4f(tint_program_layout.edges_location, crd->gl.xstart, crd->gl.ystart - crd->gl.height, crd->gl.xstart + crd->gl.width, crd->gl.ystart); + glUniform4f(tint_program_layout.uniforms.edges, crd->gl.xstart, crd->gl.ystart - crd->gl.height, crd->gl.xstart + crd->gl.width, crd->gl.ystart); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); } +static float prev_inactive_text_alpha = -1; + static void set_cell_uniforms(float current_inactive_text_alpha, bool force) { - if (!cell_uniform_data.constants_set || force) { - cell_uniform_data.gploc = glGetUniformLocation(program_id(GRAPHICS_PROGRAM), "inactive_text_alpha"); - cell_uniform_data.gpploc = glGetUniformLocation(program_id(GRAPHICS_PREMULT_PROGRAM), "inactive_text_alpha"); - cell_uniform_data.cploc = glGetUniformLocation(program_id(CELL_PROGRAM), "inactive_text_alpha"); - cell_uniform_data.cfploc = glGetUniformLocation(program_id(CELL_FG_PROGRAM), "inactive_text_alpha"); - cell_uniform_data.amask_bg_premult_loc = glGetUniformLocation(program_id(GRAPHICS_ALPHA_MASK_PROGRAM), "amask_bg_premult"); - cell_uniform_data.amask_fg_loc = glGetUniformLocation(program_id(GRAPHICS_ALPHA_MASK_PROGRAM), "amask_fg"); - cell_uniform_data.amask_image_loc = glGetUniformLocation(program_id(GRAPHICS_ALPHA_MASK_PROGRAM), "image"); -#define S(prog, name, val, type) { bind_program(prog); glUniform##type(glGetUniformLocation(program_id(prog), #name), val); } - S(GRAPHICS_PROGRAM, image, GRAPHICS_UNIT, 1i); - S(GRAPHICS_PREMULT_PROGRAM, image, GRAPHICS_UNIT, 1i); - S(CELL_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); S(CELL_FG_PROGRAM, sprites, SPRITE_MAP_UNIT, 1i); - S(CELL_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); S(CELL_FG_PROGRAM, dim_opacity, OPT(dim_opacity), 1f); - S(CELL_BG_PROGRAM, defaultbg, OPT(background), 1f); + static bool constants_set = false; + if (!constants_set || force) { float text_contrast = 1.0f + OPT(text_contrast) * 0.01f; - S(CELL_PROGRAM, text_contrast, text_contrast, 1f); S(CELL_FG_PROGRAM, text_contrast, text_contrast, 1f); float text_gamma_adjustment = OPT(text_gamma_adjustment) < 0.01f ? 1.0f : 1.0f / OPT(text_gamma_adjustment); - S(CELL_PROGRAM, text_gamma_adjustment, text_gamma_adjustment, 1f); S(CELL_FG_PROGRAM, text_gamma_adjustment, text_gamma_adjustment, 1f); -#undef S -#define SV(prog, name, num, val, type) { bind_program(prog); glUniform##type(glGetUniformLocation(program_id(prog), #name), num, val); } - SV(CELL_PROGRAM, gamma_lut, 256, srgb_lut, 1fv); SV(CELL_FG_PROGRAM, gamma_lut, 256, srgb_lut, 1fv); - SV(CELL_BG_PROGRAM, gamma_lut, 256, srgb_lut, 1fv); SV(CELL_SPECIAL_PROGRAM, gamma_lut, 256, srgb_lut, 1fv); -#undef SV - cell_uniform_data.constants_set = true; + + for (int i = GRAPHICS_PROGRAM; i <= GRAPHICS_PREMULT_PROGRAM; i++) { + bind_program(i); glUniform1i(graphics_program_layouts[i].uniforms.image, GRAPHICS_UNIT); + } + for (int i = CELL_PROGRAM; i <= CELL_FG_PROGRAM; i++) { + bind_program(i); const CellUniforms *cu = &cell_program_layouts[i].uniforms; + glUniform1fv(cu->gamma_lut, arraysz(srgb_lut), srgb_lut); + switch(i) { + case CELL_PROGRAM: case CELL_FG_PROGRAM: + glUniform1i(cu->sprites, SPRITE_MAP_UNIT); + glUniform1f(cu->dim_opacity, OPT(dim_opacity)); + glUniform1f(cu->text_contrast, text_contrast); + glUniform1f(cu->text_gamma_adjustment, text_gamma_adjustment); + break; + } + } + constants_set = true; } - if (current_inactive_text_alpha != cell_uniform_data.prev_inactive_text_alpha || force) { - cell_uniform_data.prev_inactive_text_alpha = current_inactive_text_alpha; -#define S(prog, loc) { bind_program(prog); glUniform1f(cell_uniform_data.loc, current_inactive_text_alpha); } - S(CELL_PROGRAM, cploc); S(CELL_FG_PROGRAM, cfploc); S(GRAPHICS_PROGRAM, gploc); S(GRAPHICS_PREMULT_PROGRAM, gpploc); + if (current_inactive_text_alpha != prev_inactive_text_alpha || force) { + prev_inactive_text_alpha = current_inactive_text_alpha; + for (int i = GRAPHICS_PROGRAM; i <= GRAPHICS_PREMULT_PROGRAM; i++) { + bind_program(i); glUniform1f(graphics_program_layouts[i].uniforms.inactive_text_alpha, current_inactive_text_alpha); + } +#define S(prog, loc) bind_program(prog); glUniform1f(cell_program_layouts[prog].uniforms.inactive_text_alpha, current_inactive_text_alpha); + S(CELL_PROGRAM, cploc); S(CELL_FG_PROGRAM, cfploc); #undef S } } @@ -646,10 +660,9 @@ render_a_bar(OSWindow *os_window, Screen *screen, const CellRenderData *crd, Win glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA, bar_width, bar_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bar->buf); set_cell_uniforms(1.f, false); bind_program(GRAPHICS_PROGRAM); - send_graphics_data_to_gpu(1, os_window->gvao_idx, &data); glEnable(GL_BLEND); if (os_window->is_semi_transparent) { BLEND_PREMULT; } else { BLEND_ONTO_OPAQUE; } - draw_graphics(GRAPHICS_PROGRAM, 0, os_window->gvao_idx, &data, 0, 1); + draw_graphics(GRAPHICS_PROGRAM, 0, &data, 0, 1, viewport_for_cells(crd)); glDisable(GL_BLEND); return height_gl; } @@ -687,11 +700,10 @@ draw_window_logo(ssize_t vao_idx, OSWindow *os_window, const WindowLogoRenderDat static ImageRenderData ird = {.group_count=1}; ird.texture_id = wl->instance->texture_id; gpu_data_for_image(&ird, logo_left_gl, logo_top_gl, logo_left_gl + logo_width_gl, logo_top_gl - logo_height_gl); - send_graphics_data_to_gpu(1, os_window->gvao_idx, &ird); bind_program(GRAPHICS_PREMULT_PROGRAM); - glUniform1f(cell_uniform_data.gpploc, cell_uniform_data.prev_inactive_text_alpha * wl->alpha); - draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, os_window->gvao_idx, &ird, 0, 1); - glUniform1f(cell_uniform_data.gpploc, cell_uniform_data.prev_inactive_text_alpha); + glUniform1f(graphics_program_layouts[GRAPHICS_PREMULT_PROGRAM].uniforms.inactive_text_alpha, prev_inactive_text_alpha * wl->alpha); + draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, &ird, 0, 1, viewport_for_cells(crd)); + glUniform1f(graphics_program_layouts[GRAPHICS_PREMULT_PROGRAM].uniforms.inactive_text_alpha, prev_inactive_text_alpha); } static void @@ -736,12 +748,11 @@ draw_window_number(OSWindow *os_window, Screen *screen, const CellRenderData *cr gpu_data_for_image(ird, left, top, right, bottom); glEnable(GL_BLEND); BLEND_PREMULT; - glUniform1i(cell_uniform_data.amask_image_loc, GRAPHICS_UNIT); + glUniform1i(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.image, GRAPHICS_UNIT); color_type digit_color = colorprofile_to_color_with_fallback(screen->color_profile, screen->color_profile->overridden.highlight_bg, screen->color_profile->configured.highlight_bg, screen->color_profile->overridden.default_fg, screen->color_profile->configured.default_fg); - color_vec3(cell_uniform_data.amask_fg_loc, digit_color); - glUniform4f(cell_uniform_data.amask_bg_premult_loc, 0.f, 0.f, 0.f, 0.f); - send_graphics_data_to_gpu(1, os_window->gvao_idx, ird); - draw_graphics(GRAPHICS_ALPHA_MASK_PROGRAM, 0, os_window->gvao_idx, ird, 0, 1); + color_vec3(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.amask_fg, digit_color); + glUniform4f(graphics_program_layouts[GRAPHICS_ALPHA_MASK_PROGRAM].uniforms.amask_bg_premult, 0.f, 0.f, 0.f, 0.f); + draw_graphics(GRAPHICS_ALPHA_MASK_PROGRAM, 0, ird, 0, 1, viewport_for_cells(crd)); glDisable(GL_BLEND); } @@ -761,22 +772,22 @@ draw_visual_bell_flash(GLfloat intensity, const CellRenderData *crd, Screen *scr #undef C #define C(x) (x * intensity * attenuation) if (max_channel > 0.45) attenuation = 0.6f; // light color - glUniform4f(tint_program_layout.tint_color_location, C(r), C(g), C(b), C(1)); + glUniform4f(tint_program_layout.uniforms.tint_color, C(r), C(g), C(b), C(1)); #undef C - glUniform4f(tint_program_layout.edges_location, crd->gl.xstart, crd->gl.ystart - crd->gl.height, crd->gl.xstart + crd->gl.width, crd->gl.ystart); + glUniform4f(tint_program_layout.uniforms.edges, crd->gl.xstart, crd->gl.ystart - crd->gl.height, crd->gl.xstart + crd->gl.width, crd->gl.ystart); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDisable(GL_BLEND); } static void -draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *w, const CellRenderData *crd, const WindowLogoRenderData *wl) { +draw_cells_interleaved(ssize_t vao_idx, Screen *screen, OSWindow *w, const CellRenderData *crd, const WindowLogoRenderData *wl) { glEnable(GL_BLEND); BLEND_ONTO_OPAQUE; // draw background for all cells if (!has_bgimage(w)) { bind_program(CELL_BG_PROGRAM); - glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3); + glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].uniforms.draw_bg_bitfield, 3); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); } else if (OPT(background_tint) > 0) { draw_tint(false, screen, crd); @@ -789,14 +800,14 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWind BLEND_ONTO_OPAQUE; } if (screen->grman->num_of_below_refs) draw_graphics( - GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs); + GRAPHICS_PROGRAM, vao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs, viewport_for_cells(crd)); bind_program(CELL_BG_PROGRAM); // draw background for non-default bg cells - glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 2); + glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].uniforms.draw_bg_bitfield, 2); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); } - if (screen->grman->num_of_negative_refs) draw_graphics(GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_below_refs, screen->grman->num_of_negative_refs); + if (screen->grman->num_of_negative_refs) draw_graphics(GRAPHICS_PROGRAM, vao_idx, screen->grman->render_data, screen->grman->num_of_below_refs, screen->grman->num_of_negative_refs, viewport_for_cells(crd)); bind_program(CELL_SPECIAL_PROGRAM); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); @@ -806,13 +817,13 @@ draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWind glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); BLEND_ONTO_OPAQUE; - if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs); + if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PROGRAM, vao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs, viewport_for_cells(crd)); glDisable(GL_BLEND); } static void -draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, OSWindow *os_window, const CellRenderData *crd, const WindowLogoRenderData *wl) { +draw_cells_interleaved_premult(ssize_t vao_idx, Screen *screen, OSWindow *os_window, const CellRenderData *crd, const WindowLogoRenderData *wl) { if (OPT(background_tint) > 0.f) { glEnable(GL_BLEND); draw_tint(true, screen, crd); @@ -821,7 +832,7 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen bind_program(CELL_BG_PROGRAM); if (!has_bgimage(os_window)) { // draw background for all cells - glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 3); + glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].uniforms.draw_bg_bitfield, 3); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); } glEnable(GL_BLEND); @@ -833,19 +844,19 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen BLEND_PREMULT; } if (screen->grman->num_of_below_refs) draw_graphics( - GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs); + GRAPHICS_PREMULT_PROGRAM, vao_idx, screen->grman->render_data, 0, screen->grman->num_of_below_refs, viewport_for_cells(crd)); bind_program(CELL_BG_PROGRAM); // Draw background for non-default bg cells - glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 2); + glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].uniforms.draw_bg_bitfield, 2); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); } else { // Apply background_opacity - glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].draw_bg_bitfield_location, 0); + glUniform1ui(cell_program_layouts[CELL_BG_PROGRAM].uniforms.draw_bg_bitfield, 0); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); } if (screen->grman->num_of_negative_refs) { - draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_below_refs, screen->grman->num_of_negative_refs); + draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, screen->grman->render_data, screen->grman->num_of_below_refs, screen->grman->num_of_negative_refs, viewport_for_cells(crd)); } bind_program(CELL_SPECIAL_PROGRAM); @@ -854,7 +865,7 @@ draw_cells_interleaved_premult(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen bind_program(CELL_FG_PROGRAM); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); - if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs); + if (screen->grman->num_of_positive_refs) draw_graphics(GRAPHICS_PREMULT_PROGRAM, vao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs + screen->grman->num_of_below_refs, screen->grman->num_of_positive_refs, viewport_for_cells(crd)); if (!has_bgimage(os_window)) glDisable(GL_BLEND); } @@ -869,10 +880,10 @@ blank_canvas(float background_opacity, color_type color) { } bool -send_cell_data_to_gpu(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, Screen *screen, OSWindow *os_window) { +send_cell_data_to_gpu(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, Screen *screen, OSWindow *os_window) { bool changed = false; if (os_window->fonts_data) { - if (cell_prepare_to_render(vao_idx, gvao_idx, screen, xstart, ystart, dx, dy, os_window->fonts_data)) changed = true; + if (cell_prepare_to_render(vao_idx, screen, xstart, ystart, dx, dy, os_window->fonts_data)) changed = true; } return changed; } @@ -907,18 +918,8 @@ get_visual_bell_intensity(Screen *screen) { return 0.0f; } -static void -scale_graphics_for_live_resize(Screen *screen, float xstart, float ystart, CellRenderData *crd, ssize_t gvao_idx) { - const size_t sz = screen->grman->count * sizeof(screen->grman->render_data[0]); - FREE_AFTER_FUNCTION ImageRenderData *changed = malloc(sz); - if (changed == NULL) fatal("Out of memory allocating temporary ImageRenderData* for live resize"); - memcpy(changed, screen->grman->render_data, sz); - for (size_t i = 0; i < screen->grman->count; i++) scale_rendered_graphic(changed + i, xstart, ystart, crd->x_ratio, crd->y_ratio); - send_graphics_data_to_gpu(screen->grman->count, gvao_idx, changed); -} - void -draw_cells(ssize_t vao_idx, ssize_t gvao_idx, const ScreenRenderData *srd, OSWindow *os_window, bool is_active_window, bool can_be_focused, Window *window) { +draw_cells(ssize_t vao_idx, const ScreenRenderData *srd, OSWindow *os_window, bool is_active_window, bool can_be_focused, Window *window) { float x_ratio = 1., y_ratio = 1.; if (os_window->live_resize.in_progress) { x_ratio = (float) os_window->viewport_width / (float) os_window->live_resize.width; @@ -932,21 +933,6 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, const ScreenRenderData *srd, OSWin .x_ratio=x_ratio, .y_ratio=y_ratio }; crd.gl.width = crd.gl.dx * screen->columns; crd.gl.height = crd.gl.dy * screen->lines; - // The scissor limits below are calculated to ensure that they do not - // overlap with the pixels outside the draw area. We can't use the actual pixel window dimensions - // because of the mapping of opengl's float based co-ord system to pixels. - unsigned scissor_base_width = os_window->live_resize.in_progress ? os_window->live_resize.width : (unsigned)os_window->viewport_width; - unsigned scissor_base_height = os_window->live_resize.in_progress ? os_window->live_resize.height: (unsigned)os_window->viewport_height; -#define SCALE(w, x) ((GLfloat)(scissor_base_##w) * (GLfloat)(x)) - /* printf("columns=%d dx=%f w=%f vw=%d vh=%d left=%f width=%f\n", screen->columns, dx, w, os_window->viewport_width, os_window->viewport_height, SCALE(width, (xstart + 1.f)/2.f), SCALE(width, w / 2.f)); */ - - crd.px.xstart = (GLint)roundf(SCALE(width, (crd.gl.xstart + 1.f)/2.f)); - crd.px.ystart = (GLint)roundf(SCALE(height, (crd.gl.ystart - crd.gl.height + 1.f)/2.f)); - crd.px.width = (GLsizei)roundf(SCALE(width, crd.gl.width / 2.f)); - crd.px.height = (GLsizei)roundf(SCALE(height, crd.gl.height / 2.f)); -#undef SCALE - glScissor(crd.px.xstart, crd.px.ystart, crd.px.width, crd.px.height); - cell_update_uniform_block(vao_idx, screen, uniform_buffer, &crd, &screen->cursor_render_info, inverted, os_window); bind_vao_uniform_buffer(vao_idx, uniform_buffer, cell_program_layouts[CELL_PROGRAM].render_data.index); @@ -961,19 +947,23 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, const ScreenRenderData *srd, OSWin has_underlying_image = true; set_on_gpu_state(window->window_logo.instance, true); } else wl = NULL; - bool restore_graphics_render_data = false; - if (os_window->live_resize.in_progress && window->render_data.gvao_idx && screen->grman->count && (crd.x_ratio != 1 || crd.y_ratio != 1)) { - restore_graphics_render_data = true; - scale_graphics_for_live_resize(screen, srd->xstart, srd->ystart, &crd, window->render_data.gvao_idx); + ImageRenderData *previous_graphics_render_data = NULL; + if (os_window->live_resize.in_progress && screen->grman->count && (crd.x_ratio != 1 || crd.y_ratio != 1)) { + previous_graphics_render_data = malloc(sizeof(previous_graphics_render_data[0]) * screen->grman->count); + if (previous_graphics_render_data) { + memcpy(previous_graphics_render_data, screen->grman->render_data, sizeof(previous_graphics_render_data[0]) * screen->grman->count); + for (size_t i = 0; i < screen->grman->count; i++) + scale_rendered_graphic(screen->grman->render_data + i, srd->xstart, srd->ystart, crd.x_ratio, crd.y_ratio); + } } if (os_window->is_semi_transparent) { if (screen->grman->count || has_underlying_image) draw_cells_interleaved_premult( - vao_idx, gvao_idx, screen, os_window, &crd, wl); - else draw_cells_simple(vao_idx, gvao_idx, screen); + vao_idx, screen, os_window, &crd, wl); + else draw_cells_simple(vao_idx, screen, &crd); } else { if (screen->grman->num_of_negative_refs || screen->grman->num_of_below_refs || has_underlying_image) draw_cells_interleaved( - vao_idx, gvao_idx, screen, os_window, &crd, wl); - else draw_cells_simple(vao_idx, gvao_idx, screen); + vao_idx, screen, os_window, &crd, wl); + else draw_cells_simple(vao_idx, screen, &crd); } if (screen->start_visual_bell_at) { @@ -983,24 +973,23 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, const ScreenRenderData *srd, OSWin if (window && screen->display_window_char) draw_window_number(os_window, screen, &crd, window); if (OPT(show_hyperlink_targets) && window && screen->current_hyperlink_under_mouse.id && !is_mouse_hidden(os_window)) draw_hyperlink_target(os_window, screen, &crd, window); - if (restore_graphics_render_data) send_graphics_data_to_gpu(screen->grman->count, window->render_data.gvao_idx, screen->grman->render_data); + if (previous_graphics_render_data) { + free(screen->grman->render_data); + screen->grman->render_data = previous_graphics_render_data; + } } // }}} // Borders {{{ -enum BorderUniforms { BORDER_viewport, BORDER_background_opacity, BORDER_tint_opacity, BORDER_tint_premult, BORDER_colors, BORDER_gamma_lut, NUM_BORDER_UNIFORMS }; -static GLint border_uniform_locations[NUM_BORDER_UNIFORMS] = {0}; + +typedef struct BorderProgramLayout { + BorderUniforms uniforms; +} BorderProgramLayout; +static BorderProgramLayout border_program_layout; static void init_borders_program(void) { -#define SET_LOC(which) border_uniform_locations[BORDER_##which] = get_uniform_location(BORDERS_PROGRAM, #which); - SET_LOC(viewport) - SET_LOC(background_opacity) - SET_LOC(tint_opacity) - SET_LOC(tint_premult) - SET_LOC(colors) - SET_LOC(gamma_lut) -#undef SET_LOC + get_uniform_locations_border(BORDERS_PROGRAM, &border_program_layout.uniforms); } ssize_t @@ -1046,12 +1035,12 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu OPT(bell_border_color), OPT(tab_bar_background), OPT(tab_bar_margin_color), w->tab_bar_edge_color.left, w->tab_bar_edge_color.right }; - glUniform1uiv(border_uniform_locations[BORDER_colors], arraysz(colors), colors); - glUniform1f(border_uniform_locations[BORDER_background_opacity], background_opacity); - glUniform1f(border_uniform_locations[BORDER_tint_opacity], tint_opacity); - glUniform1f(border_uniform_locations[BORDER_tint_premult], tint_premult); - glUniform2ui(border_uniform_locations[BORDER_viewport], viewport_width, viewport_height); - glUniform1fv(border_uniform_locations[BORDER_gamma_lut], 256, srgb_lut); + glUniform1uiv(border_program_layout.uniforms.colors, arraysz(colors), colors); + glUniform1f(border_program_layout.uniforms.background_opacity, background_opacity); + glUniform1f(border_program_layout.uniforms.tint_opacity, tint_opacity); + glUniform1f(border_program_layout.uniforms.tint_premult, tint_premult); + glUniform2ui(border_program_layout.uniforms.viewport, viewport_width, viewport_height); + glUniform1fv(border_program_layout.uniforms.gamma_lut, 256, srgb_lut); if (has_bgimage(w)) { if (w->is_semi_transparent) { BLEND_PREMULT; } else { BLEND_ONTO_OPAQUE_WITH_OPAQUE_OUTPUT; } diff --git a/kitty/state.c b/kitty/state.c index 61dfeef15..c8ad740d7 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -203,7 +203,6 @@ add_os_window(void) { zero_at_ptr(ans); ans->id = ++global_state.os_window_id_counter; ans->tab_bar_render_data.vao_idx = create_cell_vao(); - ans->gvao_idx = create_graphics_vao(); ans->background_opacity = OPT(background_opacity); bool wants_bg = OPT(background_image) && OPT(background_image)[0] != 0; @@ -244,15 +243,12 @@ add_tab(id_type os_window_id) { static void create_gpu_resources_for_window(Window *w) { w->render_data.vao_idx = create_cell_vao(); - w->render_data.gvao_idx = create_graphics_vao(); } static void release_gpu_resources_for_window(Window *w) { if (w->render_data.vao_idx > -1) remove_vao(w->render_data.vao_idx); w->render_data.vao_idx = -1; - if (w->render_data.gvao_idx > -1) remove_vao(w->render_data.gvao_idx); - w->render_data.gvao_idx = -1; } static bool @@ -292,7 +288,6 @@ initialize_window(Window *w, PyObject *title, bool init_gpu_resources) { if (init_gpu_resources) create_gpu_resources_for_window(w); else { w->render_data.vao_idx = -1; - w->render_data.gvao_idx = -1; } } @@ -478,7 +473,6 @@ destroy_os_window_item(OSWindow *w) { } Py_CLEAR(w->window_title); Py_CLEAR(w->tab_bar_render_data.screen); remove_vao(w->tab_bar_render_data.vao_idx); - remove_vao(w->gvao_idx); free(w->tabs); w->tabs = NULL; free_bgimage(&w->bgimage, true); w->bgimage = NULL; @@ -944,7 +938,6 @@ PYWRAP1(set_window_render_data) { WITH_WINDOW(os_window_id, tab_id, window_id); Py_CLEAR(window->render_data.screen); d.vao_idx = window->render_data.vao_idx; - d.gvao_idx = window->render_data.gvao_idx; init_screen_render_data(osw, &g, &d); window->render_data = d; window->geometry = g; diff --git a/kitty/state.h b/kitty/state.h index 8924e996b..1e87f1e2e 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -100,7 +100,7 @@ typedef struct WindowLogoRenderData { } WindowLogoRenderData; typedef struct { - ssize_t vao_idx, gvao_idx; + ssize_t vao_idx; float xstart, ystart, dx, dy; Screen *screen; } ScreenRenderData; @@ -224,7 +224,6 @@ typedef struct { monotonic_t last_render_frame_received_at; uint64_t render_calls; id_type last_focused_counter; - ssize_t gvao_idx; CloseRequest close_request; } OSWindow; @@ -290,8 +289,8 @@ void draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *re ssize_t create_cell_vao(void); ssize_t create_graphics_vao(void); ssize_t create_border_vao(void); -bool send_cell_data_to_gpu(ssize_t, ssize_t, float, float, float, float, Screen *, OSWindow *); -void draw_cells(ssize_t, ssize_t, const ScreenRenderData*, OSWindow *, bool, bool, Window*); +bool send_cell_data_to_gpu(ssize_t, float, float, float, float, Screen *, OSWindow *); +void draw_cells(ssize_t, const ScreenRenderData*, OSWindow *, bool, bool, Window*); void draw_centered_alpha_mask(OSWindow *w, size_t screen_width, size_t screen_height, size_t width, size_t height, uint8_t *canvas); void update_surface_size(int, int, uint32_t); void free_texture(uint32_t*); diff --git a/setup.py b/setup.py index 69cf12b32..f0604c369 100755 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ from contextlib import suppress from functools import lru_cache, partial from pathlib import Path -from typing import Callable, Dict, FrozenSet, Iterable, List, Optional, Sequence, Set, Tuple, Union, cast +from typing import Callable, Dict, FrozenSet, Iterable, Iterator, List, Optional, Sequence, Set, Tuple, Union, cast from glfw import glfw from glfw.glfw import Command, CompileKey @@ -864,6 +864,55 @@ def build_ref_map(skip_generation: bool = False) -> str: return dest +def build_uniforms_header(skip_generation: bool = False) -> str: + dest = 'kitty/uniforms_generated.h' + if skip_generation: + return dest + lines = ['#include "gl.h"', ''] + a = lines.append + uniform_names = {} + class_names = {} + function_names = {} + + def find_uniform_names(raw: str) -> Iterator[str]: + for m in re.finditer(r'^uniform\s+\S+\s+(.+?);', raw, flags=re.MULTILINE): + for x in m.group(1).split(','): + yield x.strip().partition('[')[0] + + for x in glob.glob('kitty/*.glsl'): + name = os.path.basename(x).partition('.')[0] + name, sep, shader_type = name.partition('_') + if not sep or shader_type not in ('fragment', 'vertex'): + continue + class_names[name] = f'{name.capitalize()}Uniforms' + function_names[name] = f'get_uniform_locations_{name}' + with open(x) as f: + raw = f.read() + uniform_names[name] = uniform_names.setdefault(name, ()) + tuple(find_uniform_names(raw)) + for name in sorted(class_names): + class_name, function_name, uniforms = class_names[name], function_names[name], uniform_names[name] + a(f'typedef struct {class_name} ''{') + for n in uniforms: + a(f' GLint {n};') + a('}'f' {class_name};') + a('') + a(f'static inline void\n{function_name}(int program, {class_name} *ans) ''{') + for n in uniforms: + a(f' ans->{n} = get_uniform_location(program, "{n}");') + a('}') + a('') + src = '\n'.join(lines) + try: + with open(dest) as f: + current = f.read() + except FileNotFoundError: + current = '' + if src != current: + with open(dest, 'w') as f: + f.write(src) + return dest + + @lru_cache def wrapped_kittens() -> str: with open('shell-integration/ssh/kitty') as f: @@ -879,6 +928,7 @@ def build(args: Options, native_optimizations: bool = True, call_init: bool = Tr init_env_from_args(args, native_optimizations) sources, headers = find_c_files() headers.append(build_ref_map(args.skip_code_generation)) + headers.append(build_uniforms_header(args.skip_code_generation)) compile_c_extension( kitty_env(args), 'kitty/fast_data_types', args.compilation_database, sources, headers )