From 66803e6873c9f8df1a75265aedc0670fce4411be Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 17 Oct 2017 12:15:52 +0530 Subject: [PATCH] Workaround to get graphics working on Apple's buggy products Apparently some macOS OpenGL drivers cannot handle using a VAO with attributes that have different divisors/apply to different shaders. So use a separate VAO for graphics rendering. This is a small performance hit, but is the price of supporting substandard computers. --- kitty/cell_vertex.glsl | 1 - kitty/child-monitor.c | 4 +-- kitty/graphics_vertex.glsl | 2 +- kitty/shaders.c | 74 +++++++++++++++----------------------- kitty/state.c | 2 +- kitty/state.h | 4 +-- kitty/window.py | 12 ++++--- 7 files changed, 41 insertions(+), 58 deletions(-) diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index 46b92c886..d7ffb249b 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -13,7 +13,6 @@ layout(std140) uniform CellRenderData { }; // Have to use fixed locations here as all variants of the cell program share the same VAO -// location 3 is used in the graphics program which uses the same VAO layout(location=0) in uvec3 colors; layout(location=1) in uvec4 sprite_coords; layout(location=2) in float is_selected; diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 8f7a0f2ea..7a51e52d3 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -508,7 +508,7 @@ render(double now) { draw_borders(); cursor_info.is_visible = false; #define TD global_state.tab_bar_render_data - if (TD.screen && global_state.num_tabs > 1) draw_cells(TD.vao_idx, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, &cursor_info); + if (TD.screen && global_state.num_tabs > 1) draw_cells(TD.vao_idx, 0, TD.xstart, TD.ystart, TD.dx, TD.dy, TD.screen, &cursor_info); #undef TD if (global_state.num_tabs) { Tab *tab = global_state.tabs + global_state.active_tab; @@ -529,7 +529,7 @@ render(double now) { collect_cursor_info(&cursor_info, w, now); update_window_title(w); } else cursor_info.is_visible = false; - draw_cells(WD.vao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, &cursor_info); + draw_cells(WD.vao_idx, WD.gvao_idx, WD.xstart, WD.ystart, WD.dx, WD.dy, WD.screen, &cursor_info); if (is_active_window && cursor_info.is_visible && cursor_info.shape != CURSOR_BLOCK) draw_cursor(&cursor_info); if (WD.screen->start_visual_bell_at != 0) { double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at); diff --git a/kitty/graphics_vertex.glsl b/kitty/graphics_vertex.glsl index 9345339b4..16eb1381a 100644 --- a/kitty/graphics_vertex.glsl +++ b/kitty/graphics_vertex.glsl @@ -1,6 +1,6 @@ #version GLSL_VERSION -layout(location=3) in vec4 src; +in vec4 src; out vec2 texcoord; void main() { diff --git a/kitty/shaders.c b/kitty/shaders.c index 1b4874222..90fcda60c 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -9,26 +9,7 @@ #include enum { CELL_PROGRAM, CELL_BACKGROUND_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FOREGROUND_PROGRAM, CURSOR_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, NUM_PROGRAMS }; -enum {SPRITE_MAP_UNIT, GRAPHICS_UNIT}; - -static bool broken_multi_vao = false; - -static inline void -detect_broken_multi_vao() { -#ifdef __APPLE__ - char str[256] = {0}; - size_t size = sizeof(str); - int mv = 0; - if (sysctlbyname("kern.osrelease", str, &size, NULL, 0) != 0) broken_multi_vao = true; - else { - mv = atoi(str); - broken_multi_vao = mv < 17; - } - if (broken_multi_vao) { - fprintf(stderr, "WARNING: Old macOS version: %d detected, disabling graphics support!\n", mv); - } -#endif -} +enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT }; // Sprites {{{ typedef struct { @@ -215,12 +196,11 @@ init_cell_program() { for (int p = CELL_PROGRAM; p <= CELL_FOREGROUND_PROGRAM; p++) { C(p, colors, 0); C(p, sprite_coords, 1); C(p, is_selected, 2); } - C(GRAPHICS_PROGRAM, src, 3); #undef C } -#define CELL_BUFFERS enum { cell_data_buffer, selection_buffer, uniform_buffer, graphics_buffer }; +#define CELL_BUFFERS enum { cell_data_buffer, selection_buffer, uniform_buffer }; static ssize_t create_cell_vao() { @@ -229,7 +209,6 @@ create_cell_vao() { add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \ /*size=*/size, /*dtype=*/dtype, /*stride=*/stride, /*offset=*/offset, /*divisor=*/1); #define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(Cell, offset)), sizeof(Cell)) -#define AL(p, name, size, dtype, offset, stride) { GLint aloc = attrib_location(p, #name); if (aloc == -1 ) fatal("No attribute named: %s found in this program", #name); add_located_attribute_to_vao(vao_idx, aloc, size, dtype, stride, offset, 0); } add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); A1(sprite_coords, 4, GL_UNSIGNED_SHORT, sprite_x); @@ -241,15 +220,17 @@ create_cell_vao() { size_t bufnum = add_buffer_to_vao(vao_idx, GL_UNIFORM_BUFFER); alloc_vao_buffer(vao_idx, cell_program_layouts[CELL_PROGRAM].render_data.size, bufnum, GL_STREAM_DRAW); - if (!broken_multi_vao) { - add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); - AL(GRAPHICS_PROGRAM, src, 4, GL_FLOAT, NULL, 0); - } - return vao_idx; #undef A #undef A1 -#undef AL +} + +static ssize_t +create_graphics_vao() { + ssize_t vao_idx = create_vao(); + add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); + add_attribute_to_vao(GRAPHICS_PROGRAM, vao_idx, "src", 4, GL_FLOAT, 0, NULL, 0); + return vao_idx; } static inline void @@ -297,7 +278,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G } static inline void -cell_prepare_to_render(ssize_t vao_idx, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor) { +cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, CursorRenderInfo *cursor) { size_t sz; CELL_BUFFERS; void *address; @@ -315,11 +296,11 @@ cell_prepare_to_render(ssize_t vao_idx, Screen *screen, GLfloat xstart, GLfloat unmap_vao_buffer(vao_idx, selection_buffer); address = NULL; } - if (grman_update_layers(screen->grman, screen->scrolled_by, xstart, ystart, dx, dy, screen->columns, screen->lines)) { + if (gvao_idx && grman_update_layers(screen->grman, screen->scrolled_by, xstart, ystart, dx, dy, screen->columns, screen->lines)) { sz = sizeof(GLfloat) * 16 * screen->grman->count; - GLfloat *a = alloc_and_map_vao_buffer(vao_idx, sz, graphics_buffer, GL_STREAM_DRAW, GL_WRITE_ONLY); + GLfloat *a = alloc_and_map_vao_buffer(gvao_idx, sz, 0, GL_STREAM_DRAW, GL_WRITE_ONLY); for (size_t i = 0; i < screen->grman->count; i++, a += 16) memcpy(a, screen->grman->render_data[i].vertices, sizeof(screen->grman->render_data[0].vertices)); - unmap_vao_buffer(vao_idx, graphics_buffer); a = NULL; + unmap_vao_buffer(gvao_idx, 0); a = NULL; } cell_update_uniform_block(vao_idx, screen, uniform_buffer, xstart, ystart, dx, dy, cursor); @@ -332,7 +313,8 @@ cell_prepare_to_render(ssize_t vao_idx, Screen *screen, GLfloat xstart, GLfloat } static void -draw_graphics(ImageRenderData *data, GLuint start, GLuint count) { +draw_graphics(ssize_t vao_idx, ssize_t gvao_idx, ImageRenderData *data, GLuint start, GLuint count) { + bind_vertex_array(gvao_idx); bind_program(GRAPHICS_PROGRAM); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); static bool graphics_constants_set = false; @@ -353,11 +335,11 @@ draw_graphics(ImageRenderData *data, GLuint start, GLuint count) { for (GLuint k=0; k < rd->group_count; k++, base += 4, i++) glDrawArrays(GL_TRIANGLE_FAN, base, 4); } glDisable(GL_SCISSOR_TEST); - + bind_vertex_array(vao_idx); } static void -draw_all_cells(Screen *screen) { +draw_all_cells(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) { bind_program(CELL_PROGRAM); static bool cell_constants_set = false; if (!cell_constants_set) { @@ -365,15 +347,15 @@ draw_all_cells(Screen *screen) { cell_constants_set = true; } glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); check_gl(); - if (screen->grman->count) draw_graphics(screen->grman->render_data, 0, screen->grman->count); + if (screen->grman->count) draw_graphics(vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->count); } static void -draw_cells_interleaved(Screen *screen) { +draw_cells_interleaved(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) { bind_program(CELL_BACKGROUND_PROGRAM); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); check_gl(); - if (screen->grman->num_of_negative_refs) draw_graphics(screen->grman->render_data, 0, screen->grman->num_of_negative_refs); + if (screen->grman->num_of_negative_refs) draw_graphics(vao_idx, gvao_idx, screen->grman->render_data, 0, screen->grman->num_of_negative_refs); bind_program(CELL_SPECIAL_PROGRAM); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); check_gl(); @@ -382,11 +364,11 @@ draw_cells_interleaved(Screen *screen) { glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); check_gl(); - if (screen->grman->num_of_positive_refs) draw_graphics(screen->grman->render_data, screen->grman->num_of_negative_refs, screen->grman->num_of_positive_refs); + if (screen->grman->num_of_positive_refs) draw_graphics(vao_idx, gvao_idx, screen->grman->render_data, screen->grman->num_of_negative_refs, screen->grman->num_of_positive_refs); } static void -draw_cells_impl(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, Screen *screen, CursorRenderInfo *cursor) { +draw_cells_impl(ssize_t vao_idx, ssize_t gvao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLfloat dy, Screen *screen, CursorRenderInfo *cursor) { GLfloat h = (GLfloat)screen->lines * dy; #define SCALE(w, x) ((GLfloat)(global_state.viewport_##w) * (GLfloat)(x)) glScissor( @@ -396,10 +378,9 @@ draw_cells_impl(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLf (GLsizei)(ceilf(SCALE(height, h / 2.0f))) ); #undef SCALE - cell_prepare_to_render(vao_idx, screen, xstart, ystart, dx, dy, cursor); - if (screen->grman->num_of_negative_refs) draw_cells_interleaved(screen); - else draw_all_cells(screen); - + cell_prepare_to_render(vao_idx, gvao_idx, screen, xstart, ystart, dx, dy, cursor); + if (screen->grman->num_of_negative_refs) draw_cells_interleaved(vao_idx, gvao_idx, screen); + else draw_all_cells(vao_idx, gvao_idx, screen); } // }}} @@ -566,6 +547,7 @@ TWO_INT(send_borders_rects) NO_ARG(init_cell_program) NO_ARG_INT(create_cell_vao) +NO_ARG_INT(create_graphics_vao) NO_ARG(destroy_sprite_map) PYWRAP1(layout_sprite_map) { unsigned int cell_width, cell_height; @@ -630,6 +612,7 @@ static PyMethodDef module_methods[] = { MW(send_borders_rects, METH_VARARGS), MW(init_cell_program, METH_NOARGS), MW(create_cell_vao, METH_NOARGS), + MW(create_graphics_vao, METH_NOARGS), MW(layout_sprite_map, METH_VARARGS), MW(destroy_sprite_map, METH_NOARGS), MW(clear_buffers, METH_VARARGS), @@ -681,7 +664,6 @@ init_shaders(PyObject *module) { draw_cursor = &draw_cursor_impl; free_texture = &free_texture_impl; send_image_to_gpu = &send_image_to_gpu_impl; - detect_broken_multi_vao(); return true; } // }}} diff --git a/kitty/state.c b/kitty/state.c index 7ec0bc87b..375c0373e 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -197,7 +197,7 @@ PYWRAP1(set_window_render_data) { unsigned int window_idx, tab_id; static ScreenRenderData d = {0}; static WindowGeometry g = {0}; - PA("IIiffffOIIII", &tab_id, &window_idx, A(vao_idx), A(xstart), A(ystart), A(dx), A(dy), A(screen), B(left), B(top), B(right), B(bottom)); + PA("IIiiffffOIIII", &tab_id, &window_idx, A(vao_idx), A(gvao_idx), A(xstart), A(ystart), A(dx), A(dy), A(screen), B(left), B(top), B(right), B(bottom)); WITH_TAB(tab_id); Py_CLEAR(tab->windows[window_idx].render_data.screen); diff --git a/kitty/state.h b/kitty/state.h index 9e6bf0241..d34374698 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -21,7 +21,7 @@ typedef struct { } Options; typedef struct { - ssize_t vao_idx; + ssize_t vao_idx, gvao_idx; float xstart, ystart, dx, dy; Screen *screen; } ScreenRenderData; @@ -98,7 +98,7 @@ bool drag_scroll(Window *); #define EXTERNAL_FUNC(name, ret, ...) typedef ret (*name##_func)(__VA_ARGS__); extern name##_func name #define EXTERNAL_FUNC0(name, ret) typedef ret (*name##_func)(); extern name##_func name EXTERNAL_FUNC0(draw_borders, void); -EXTERNAL_FUNC(draw_cells, void, ssize_t, float, float, float, float, Screen *, CursorRenderInfo *); +EXTERNAL_FUNC(draw_cells, void, ssize_t, ssize_t, float, float, float, float, Screen *, CursorRenderInfo *); EXTERNAL_FUNC(draw_cursor, void, CursorRenderInfo *); EXTERNAL_FUNC(update_viewport_size, void, int, int); EXTERNAL_FUNC(free_texture, void, uint32_t*); diff --git a/kitty/window.py b/kitty/window.py index f187f3447..1eebcb700 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -17,9 +17,9 @@ from .fast_data_types import ( BRACKETED_PASTE_END, BRACKETED_PASTE_START, CELL_BACKGROUND_PROGRAM, CELL_FOREGROUND_PROGRAM, CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CURSOR_PROGRAM, GRAPHICS_PROGRAM, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, - Screen, compile_program, create_cell_vao, glfw_post_empty_event, - init_cell_program, init_cursor_program, remove_vao, set_window_render_data, - update_window_title, update_window_visibility + Screen, compile_program, create_cell_vao, create_graphics_vao, + glfw_post_empty_event, init_cell_program, init_cursor_program, remove_vao, + set_window_render_data, update_window_title, update_window_visibility ) from .rgb import to_color from .terminfo import get_capabilities @@ -70,6 +70,7 @@ class Window: def __init__(self, tab, child, opts, args): self.id = next(window_counter) self.vao_id = create_cell_vao() + self.gvao_id = create_graphics_vao() self.tab_id = tab.id self.tabref = weakref.ref(tab) self.override_title = None @@ -120,7 +121,7 @@ class Window: else: sg = self.update_position(new_geometry) self.geometry = g = new_geometry - set_window_render_data(self.tab_id, window_idx, self.vao_id, sg.xstart, sg.ystart, sg.dx, sg.dy, self.screen, *g[:4]) + set_window_render_data(self.tab_id, window_idx, self.vao_id, self.gvao_id, sg.xstart, sg.ystart, sg.dx, sg.dy, self.screen, *g[:4]) def contains(self, x, y): g = self.geometry @@ -230,7 +231,8 @@ class Window: def destroy(self): if self.vao_id is not None: remove_vao(self.vao_id) - self.vao_id = None + remove_vao(self.gvao_id) + self.vao_id = self.gvao_id = None # actions {{{