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.
This commit is contained in:
Kovid Goyal 2017-10-17 12:15:52 +05:30
parent 40722f42d3
commit 66803e6873
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 41 additions and 58 deletions

View File

@ -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 // 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=0) in uvec3 colors;
layout(location=1) in uvec4 sprite_coords; layout(location=1) in uvec4 sprite_coords;
layout(location=2) in float is_selected; layout(location=2) in float is_selected;

View File

@ -508,7 +508,7 @@ render(double now) {
draw_borders(); draw_borders();
cursor_info.is_visible = false; cursor_info.is_visible = false;
#define TD global_state.tab_bar_render_data #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 #undef TD
if (global_state.num_tabs) { if (global_state.num_tabs) {
Tab *tab = global_state.tabs + global_state.active_tab; Tab *tab = global_state.tabs + global_state.active_tab;
@ -529,7 +529,7 @@ render(double now) {
collect_cursor_info(&cursor_info, w, now); collect_cursor_info(&cursor_info, w, now);
update_window_title(w); update_window_title(w);
} else cursor_info.is_visible = false; } 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 (is_active_window && cursor_info.is_visible && cursor_info.shape != CURSOR_BLOCK) draw_cursor(&cursor_info);
if (WD.screen->start_visual_bell_at != 0) { if (WD.screen->start_visual_bell_at != 0) {
double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at); double bell_left = global_state.opts.visual_bell_duration - (now - WD.screen->start_visual_bell_at);

View File

@ -1,6 +1,6 @@
#version GLSL_VERSION #version GLSL_VERSION
layout(location=3) in vec4 src; in vec4 src;
out vec2 texcoord; out vec2 texcoord;
void main() { void main() {

View File

@ -9,26 +9,7 @@
#include <sys/sysctl.h> #include <sys/sysctl.h>
enum { CELL_PROGRAM, CELL_BACKGROUND_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FOREGROUND_PROGRAM, CURSOR_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, NUM_PROGRAMS }; 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}; 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
}
// Sprites {{{ // Sprites {{{
typedef struct { typedef struct {
@ -215,12 +196,11 @@ init_cell_program() {
for (int p = CELL_PROGRAM; p <= CELL_FOREGROUND_PROGRAM; p++) { 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(p, colors, 0); C(p, sprite_coords, 1); C(p, is_selected, 2);
} }
C(GRAPHICS_PROGRAM, src, 3);
#undef C #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 static ssize_t
create_cell_vao() { create_cell_vao() {
@ -229,7 +209,6 @@ create_cell_vao() {
add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \ add_attribute_to_vao(CELL_PROGRAM, vao_idx, #name, \
/*size=*/size, /*dtype=*/dtype, /*stride=*/stride, /*offset=*/offset, /*divisor=*/1); /*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 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); add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);
A1(sprite_coords, 4, GL_UNSIGNED_SHORT, sprite_x); 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); 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); 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; return vao_idx;
#undef A #undef A
#undef A1 #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 static inline void
@ -297,7 +278,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
} }
static inline void 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; size_t sz;
CELL_BUFFERS; CELL_BUFFERS;
void *address; 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; 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; 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)); 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); 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 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); bind_program(GRAPHICS_PROGRAM);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
static bool graphics_constants_set = false; 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); for (GLuint k=0; k < rd->group_count; k++, base += 4, i++) glDrawArrays(GL_TRIANGLE_FAN, base, 4);
} }
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
bind_vertex_array(vao_idx);
} }
static void static void
draw_all_cells(Screen *screen) { draw_all_cells(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen) {
bind_program(CELL_PROGRAM); bind_program(CELL_PROGRAM);
static bool cell_constants_set = false; static bool cell_constants_set = false;
if (!cell_constants_set) { if (!cell_constants_set) {
@ -365,15 +347,15 @@ draw_all_cells(Screen *screen) {
cell_constants_set = true; cell_constants_set = true;
} }
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); check_gl(); 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 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); bind_program(CELL_BACKGROUND_PROGRAM);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); check_gl(); 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); bind_program(CELL_SPECIAL_PROGRAM);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); check_gl(); 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); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, screen->lines * screen->columns); check_gl(); 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 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; GLfloat h = (GLfloat)screen->lines * dy;
#define SCALE(w, x) ((GLfloat)(global_state.viewport_##w) * (GLfloat)(x)) #define SCALE(w, x) ((GLfloat)(global_state.viewport_##w) * (GLfloat)(x))
glScissor( 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))) (GLsizei)(ceilf(SCALE(height, h / 2.0f)))
); );
#undef SCALE #undef SCALE
cell_prepare_to_render(vao_idx, screen, xstart, ystart, dx, dy, cursor); cell_prepare_to_render(vao_idx, gvao_idx, screen, xstart, ystart, dx, dy, cursor);
if (screen->grman->num_of_negative_refs) draw_cells_interleaved(screen); if (screen->grman->num_of_negative_refs) draw_cells_interleaved(vao_idx, gvao_idx, screen);
else draw_all_cells(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(init_cell_program)
NO_ARG_INT(create_cell_vao) NO_ARG_INT(create_cell_vao)
NO_ARG_INT(create_graphics_vao)
NO_ARG(destroy_sprite_map) NO_ARG(destroy_sprite_map)
PYWRAP1(layout_sprite_map) { PYWRAP1(layout_sprite_map) {
unsigned int cell_width, cell_height; unsigned int cell_width, cell_height;
@ -630,6 +612,7 @@ static PyMethodDef module_methods[] = {
MW(send_borders_rects, METH_VARARGS), MW(send_borders_rects, METH_VARARGS),
MW(init_cell_program, METH_NOARGS), MW(init_cell_program, METH_NOARGS),
MW(create_cell_vao, METH_NOARGS), MW(create_cell_vao, METH_NOARGS),
MW(create_graphics_vao, METH_NOARGS),
MW(layout_sprite_map, METH_VARARGS), MW(layout_sprite_map, METH_VARARGS),
MW(destroy_sprite_map, METH_NOARGS), MW(destroy_sprite_map, METH_NOARGS),
MW(clear_buffers, METH_VARARGS), MW(clear_buffers, METH_VARARGS),
@ -681,7 +664,6 @@ init_shaders(PyObject *module) {
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; send_image_to_gpu = &send_image_to_gpu_impl;
detect_broken_multi_vao();
return true; return true;
} }
// }}} // }}}

View File

@ -197,7 +197,7 @@ PYWRAP1(set_window_render_data) {
unsigned int window_idx, tab_id; unsigned int window_idx, tab_id;
static ScreenRenderData d = {0}; static ScreenRenderData d = {0};
static WindowGeometry g = {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); WITH_TAB(tab_id);
Py_CLEAR(tab->windows[window_idx].render_data.screen); Py_CLEAR(tab->windows[window_idx].render_data.screen);

View File

@ -21,7 +21,7 @@ typedef struct {
} Options; } Options;
typedef struct { typedef struct {
ssize_t vao_idx; ssize_t vao_idx, gvao_idx;
float xstart, ystart, dx, dy; float xstart, ystart, dx, dy;
Screen *screen; Screen *screen;
} ScreenRenderData; } 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_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 #define EXTERNAL_FUNC0(name, ret) typedef ret (*name##_func)(); extern name##_func name
EXTERNAL_FUNC0(draw_borders, void); 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(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*);

View File

@ -17,9 +17,9 @@ from .fast_data_types import (
BRACKETED_PASTE_END, BRACKETED_PASTE_START, CELL_BACKGROUND_PROGRAM, BRACKETED_PASTE_END, BRACKETED_PASTE_START, CELL_BACKGROUND_PROGRAM,
CELL_FOREGROUND_PROGRAM, CELL_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FOREGROUND_PROGRAM, CELL_PROGRAM, CELL_SPECIAL_PROGRAM,
CURSOR_PROGRAM, GRAPHICS_PROGRAM, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE, CURSOR_PROGRAM, GRAPHICS_PROGRAM, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
Screen, compile_program, create_cell_vao, glfw_post_empty_event, Screen, compile_program, create_cell_vao, create_graphics_vao,
init_cell_program, init_cursor_program, remove_vao, set_window_render_data, glfw_post_empty_event, init_cell_program, init_cursor_program, remove_vao,
update_window_title, update_window_visibility set_window_render_data, update_window_title, update_window_visibility
) )
from .rgb import to_color from .rgb import to_color
from .terminfo import get_capabilities from .terminfo import get_capabilities
@ -70,6 +70,7 @@ class Window:
def __init__(self, tab, child, opts, args): def __init__(self, tab, child, opts, args):
self.id = next(window_counter) self.id = next(window_counter)
self.vao_id = create_cell_vao() self.vao_id = create_cell_vao()
self.gvao_id = create_graphics_vao()
self.tab_id = tab.id self.tab_id = tab.id
self.tabref = weakref.ref(tab) self.tabref = weakref.ref(tab)
self.override_title = None self.override_title = None
@ -120,7 +121,7 @@ class Window:
else: else:
sg = self.update_position(new_geometry) sg = self.update_position(new_geometry)
self.geometry = g = 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): def contains(self, x, y):
g = self.geometry g = self.geometry
@ -230,7 +231,8 @@ class Window:
def destroy(self): def destroy(self):
if self.vao_id is not None: if self.vao_id is not None:
remove_vao(self.vao_id) remove_vao(self.vao_id)
self.vao_id = None remove_vao(self.gvao_id)
self.vao_id = self.gvao_id = None
# actions {{{ # actions {{{