Infrastructure for loading shaders from multiple source files

Can eventually be used to share source code (functions) across many shaders
This commit is contained in:
Kovid Goyal 2023-06-13 12:55:00 +05:30
parent 1ccfa8cda6
commit 444ec2484d
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
4 changed files with 66 additions and 37 deletions

View file

@ -458,7 +458,7 @@ def add_window(os_window_id: int, tab_id: int, title: str) -> int:
def compile_program(
which: int, vertex_shader: str, fragment_shader: str, allow_recompile: bool = False
which: int, vertex_shaders: Tuple[str, ...], fragment_shaders: Tuple[str, ...], allow_recompile: bool = False
) -> int:
pass

View file

@ -1070,12 +1070,25 @@ draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_bu
// }}}
// Python API {{{
static bool
attach_shaders(PyObject *sources, GLuint program_id, GLenum shader_type) {
for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(sources); i++) {
PyObject *temp = PyTuple_GET_ITEM(sources, i);
if (!PyUnicode_Check(temp)) { PyErr_SetString(PyExc_TypeError, "shaders must be strings"); return false; }
const char *vertex_shader = PyUnicode_AsUTF8(temp);
GLuint shader_id = compile_shader(shader_type, vertex_shader);
glAttachShader(program_id, shader_id);
}
return true;
}
static PyObject*
compile_program(PyObject UNUSED *self, PyObject *args) {
const char *vertex_shader, *fragment_shader;
PyObject *vertex_shaders, *fragment_shaders;
int which, allow_recompile = 0;
GLuint vertex_shader_id = 0, fragment_shader_id = 0;
if (!PyArg_ParseTuple(args, "iss|p", &which, &vertex_shader, &fragment_shader, &allow_recompile)) return NULL;
if (!PyArg_ParseTuple(args, "iO!O!|p", &which, &PyTuple_Type, &vertex_shaders, &PyTuple_Type, &fragment_shaders, &allow_recompile)) return NULL;
if (which < 0 || which >= NUM_PROGRAMS) { PyErr_Format(PyExc_ValueError, "Unknown program: %d", which); return NULL; }
Program *program = program_ptr(which);
if (program->id != 0) {
@ -1083,9 +1096,8 @@ compile_program(PyObject UNUSED *self, PyObject *args) {
else { PyErr_SetString(PyExc_ValueError, "program already compiled"); return NULL; }
}
program->id = glCreateProgram();
vertex_shader_id = compile_shader(GL_VERTEX_SHADER, vertex_shader);
fragment_shader_id = compile_shader(GL_FRAGMENT_SHADER, fragment_shader);
glAttachShader(program->id, vertex_shader_id);
if (!attach_shaders(vertex_shaders, program->id, GL_VERTEX_SHADER)) return NULL;
if (!attach_shaders(fragment_shaders, program->id, GL_FRAGMENT_SHADER)) return NULL;
glAttachShader(program->id, fragment_shader_id);
glLinkProgram(program->id);
GLint ret = GL_FALSE;

View file

@ -104,12 +104,25 @@ def platform_window_id(os_window_id: int) -> Optional[int]:
return None
def load_shaders(name: str, vertex_name: str = '', fragment_name: str = '') -> Tuple[str, str]:
def load_shaders(name: str, vertex_name: str = '', fragment_name: str = '') -> Tuple[Tuple[str, ...], Tuple[str, ...]]:
from .fast_data_types import GLSL_VERSION
pat = re.compile(r'^#pragma kitty_include_shader <(.+?)>', re.MULTILINE)
def load(which: str, lname: str = '') -> str:
def load_source(name: str) -> str:
return read_kitty_resource(name).decode('utf-8').replace('GLSL_VERSION', str(GLSL_VERSION), 1)
def load_sources(name: str) -> Tuple[str, ...]:
src = load_source(name)
ans: Tuple[str, ...] = src,
for m in pat.finditer(src):
iname = m.group(1)
ans += load_sources(iname)
return ans
def load(which: str, lname: str = '') -> Tuple[str, ...]:
lname = lname or name
return read_kitty_resource(f'{lname}_{which}.glsl').decode('utf-8').replace('GLSL_VERSION', str(GLSL_VERSION), 1)
main = f'{lname}_{which}.glsl'
return load_sources(main)
return load('vertex', vertex_name), load('fragment', fragment_name)

View file

@ -393,7 +393,7 @@ class LoadShaderPrograms:
self.text_old_gamma = opts.text_composition_strategy == 'legacy'
self.text_fg_override_threshold = max(0, min(opts.text_fg_override_threshold, 100)) * 0.01
compile_program(BLIT_PROGRAM, *load_shaders('blit'), allow_recompile)
v, f = load_shaders('cell')
vs, fs = load_shaders('cell')
for which, p in {
'SIMPLE': CELL_PROGRAM,
@ -401,41 +401,45 @@ class LoadShaderPrograms:
'SPECIAL': CELL_SPECIAL_PROGRAM,
'FOREGROUND': CELL_FG_PROGRAM,
}.items():
ff = f.replace('{WHICH_PROGRAM}', which)
vv = multi_replace(
v,
WHICH_PROGRAM=which,
REVERSE_SHIFT=REVERSE,
STRIKE_SHIFT=STRIKETHROUGH,
DIM_SHIFT=DIM,
DECORATION_SHIFT=DECORATION,
MARK_SHIFT=MARK,
MARK_MASK=MARK_MASK,
DECORATION_MASK=DECORATION_MASK,
STRIKE_SPRITE_INDEX=NUM_UNDERLINE_STYLES + 1,
)
if self.text_fg_override_threshold != 0.:
ff = ff.replace('#define NO_FG_OVERRIDE', f'#define FG_OVERRIDE {self.text_fg_override_threshold}')
if self.text_old_gamma:
ff = ff.replace('#define TEXT_NEW_GAMMA', '#define TEXT_OLD_GAMMA')
if semi_transparent:
vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
ff = ff.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
compile_program(p, vv, ff, allow_recompile)
vvs, ffs = [], []
for v in vs:
vv = multi_replace(
v,
WHICH_PROGRAM=which,
REVERSE_SHIFT=REVERSE,
STRIKE_SHIFT=STRIKETHROUGH,
DIM_SHIFT=DIM,
DECORATION_SHIFT=DECORATION,
MARK_SHIFT=MARK,
MARK_MASK=MARK_MASK,
DECORATION_MASK=DECORATION_MASK,
STRIKE_SPRITE_INDEX=NUM_UNDERLINE_STYLES + 1,
)
if semi_transparent:
vv = vv.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
vvs.append(vv)
for f in fs:
ff = f.replace('{WHICH_PROGRAM}', which)
if self.text_fg_override_threshold != 0.:
ff = ff.replace('#define NO_FG_OVERRIDE', f'#define FG_OVERRIDE {self.text_fg_override_threshold}')
if self.text_old_gamma:
ff = ff.replace('#define TEXT_NEW_GAMMA', '#define TEXT_OLD_GAMMA')
if semi_transparent:
ff = ff.replace('#define NOT_TRANSPARENT', '#define TRANSPARENT')
ffs.append(ff)
compile_program(p, tuple(vvs), tuple(ffs), allow_recompile)
v, f = load_shaders('graphics')
vs, fs = load_shaders('graphics')
for which, p in {
'SIMPLE': GRAPHICS_PROGRAM,
'PREMULT': GRAPHICS_PREMULT_PROGRAM,
'ALPHA_MASK': GRAPHICS_ALPHA_MASK_PROGRAM,
}.items():
ff = f.replace('ALPHA_TYPE', which)
compile_program(p, v, ff, allow_recompile)
compile_program(p, vs, tuple(f.replace('ALPHA_TYPE', which) for f in fs), allow_recompile)
v, f = load_shaders('bgimage')
compile_program(BGIMAGE_PROGRAM, v, f, allow_recompile)
v, f = load_shaders('tint')
compile_program(TINT_PROGRAM, v, f, allow_recompile)
compile_program(BGIMAGE_PROGRAM, *load_shaders('bgimage'), allow_recompile)
compile_program(TINT_PROGRAM, *load_shaders('tint'), allow_recompile)
init_cell_program()