From 9c184c794a1ecde672ca224f5e2030a4ac392046 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 10 Sep 2017 17:40:05 +0530 Subject: [PATCH] Migrate function to compile programs --- kitty/borders.py | 5 ++-- kitty/char_grid.py | 14 +++++----- kitty/shaders.c | 66 +++++++++++++++++++++++++++++++++++++++++++--- kitty/shaders.py | 64 ++++++++++++-------------------------------- 4 files changed, 90 insertions(+), 59 deletions(-) diff --git a/kitty/borders.py b/kitty/borders.py index ba8f05093..38bad0cad 100644 --- a/kitty/borders.py +++ b/kitty/borders.py @@ -8,7 +8,8 @@ from itertools import chain from .constants import GLfloat, GLint, GLuint, viewport_size from .fast_data_types import ( - GL_STATIC_DRAW, GL_TRIANGLE_FAN, glMultiDrawArrays, glUniform3fv + GL_STATIC_DRAW, GL_TRIANGLE_FAN, glMultiDrawArrays, glUniform3fv, + BORDERS_PROGRAM ) from .shaders import ShaderProgram, load_shaders from .utils import pt_to_px @@ -33,7 +34,7 @@ def as_rect(left, top, right, bottom, color=0): class BordersProgram(ShaderProgram): def __init__(self): - ShaderProgram.__init__(self, *load_shaders('border')) + ShaderProgram.__init__(self, BORDERS_PROGRAM, *load_shaders('border')) with self.array_object_creator() as add_attribute: self.vao_id = add_attribute.vao_id add_attribute('rect') diff --git a/kitty/char_grid.py b/kitty/char_grid.py index 9cf542f5a..031436d22 100644 --- a/kitty/char_grid.py +++ b/kitty/char_grid.py @@ -12,11 +12,11 @@ from .constants import ( GLfloat, GLuint, ScreenGeometry, cell_size, viewport_size ) from .fast_data_types import ( - CELL, CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE, GL_BLEND, GL_FLOAT, - GL_LINE_LOOP, GL_STATIC_DRAW, GL_TRIANGLE_FAN, GL_UNSIGNED_INT, - GL_UNSIGNED_SHORT, glDisable, glDrawArrays, glDrawArraysInstanced, - glEnable, glUniform1i, glUniform2f, glUniform2i, glUniform2ui, glUniform4f, - glUniform4ui + CELL, CELL_PROGRAM, CURSOR_BEAM, CURSOR_BLOCK, CURSOR_PROGRAM, + CURSOR_UNDERLINE, GL_BLEND, GL_FLOAT, GL_LINE_LOOP, GL_STATIC_DRAW, + GL_TRIANGLE_FAN, GL_UNSIGNED_INT, GL_UNSIGNED_SHORT, glDisable, + glDrawArrays, glDrawArraysInstanced, glEnable, glUniform1i, glUniform2f, + glUniform2i, glUniform2ui, glUniform4f, glUniform4ui ) from .rgb import to_color from .shaders import ShaderProgram, load_shaders @@ -55,8 +55,8 @@ class CellProgram(ShaderProgram): # {{{ def load_shader_programs(): - cell = CellProgram(*load_shaders('cell')) - cursor = ShaderProgram(*load_shaders('cursor')) + cell = CellProgram(CELL_PROGRAM, *load_shaders('cell')) + cursor = ShaderProgram(CURSOR_PROGRAM, *load_shaders('cursor')) with cursor.array_object_creator() as add_attribute: cursor.vao_id = add_attribute.vao_id return cell, cursor diff --git a/kitty/shaders.c b/kitty/shaders.c index fde0ff9ec..1e894fbb1 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -71,19 +71,79 @@ glew_init(PyObject UNUSED *self) { enum Program { CELL_PROGRAM, CURSOR_PROGRAM, BORDERS_PROGRAM, NUM_PROGRAMS }; -/* static GLuint program_ids[NUM_PROGRAMS] = {0}; */ +static GLuint program_ids[NUM_PROGRAMS] = {0}; +static char glbuf[4096]; +static inline GLuint +compile_shader(GLenum shader_type, const char *source) { + GLuint shader_id = glCreateShader(shader_type); + if (!shader_id) { set_error_from_gl(); return 0; } + glShaderSource(shader_id, 1, (const GLchar **)&source, NULL); + if (set_error_from_gl()) { glDeleteShader(shader_id); return 0; } + glCompileShader(shader_id); + if (set_error_from_gl()) { glDeleteShader(shader_id); return 0; } + GLint ret = GL_FALSE; + glGetShaderiv(shader_id, GL_COMPILE_STATUS, &ret); + if (ret != GL_TRUE) { + GLsizei len; + glGetShaderInfoLog(shader_id, sizeof(glbuf), &len, glbuf); + fprintf(stderr, "Failed to compile GLSL shader!\n%s", glbuf); + glDeleteShader(shader_id); + PyErr_SetString(PyExc_ValueError, "Failed to compile shader"); + return 0; + } + return shader_id; +} // Python API {{{ static PyObject* -enable_automatic_error_checking(PyObject UNUSED *self, PyObject *val) { +enable_automatic_opengl_error_checking(PyObject UNUSED *self, PyObject *val) { _enable_error_checking = PyObject_IsTrue(val) ? true : false; Py_RETURN_NONE; } +static PyObject* +compile_program(PyObject UNUSED *self, PyObject *args) { + const char *vertex_shader, *fragment_shader; + int which; + GLuint vertex_shader_id = 0, fragment_shader_id = 0; + if (!PyArg_ParseTuple(args, "iss", &which, &vertex_shader, &fragment_shader)) return NULL; + if (which < CELL_PROGRAM || which >= NUM_PROGRAMS) { PyErr_Format(PyExc_ValueError, "Unknown program: %d", which); return NULL; } + if (program_ids[which] != 0) { PyErr_SetString(PyExc_ValueError, "program already compiled"); return NULL; } + program_ids[which] = glCreateProgram(); + if (program_ids[which] == 0) { set_error_from_gl(); return NULL; } + vertex_shader_id = compile_shader(GL_VERTEX_SHADER, vertex_shader); + if (vertex_shader_id == 0) goto end; + fragment_shader_id = compile_shader(GL_FRAGMENT_SHADER, fragment_shader); + if (vertex_shader_id == 0) goto end; + glAttachShader(program_ids[which], vertex_shader_id); + if (set_error_from_gl()) goto end; + glAttachShader(program_ids[which], fragment_shader_id); + glLinkProgram(program_ids[which]); + GLint ret = GL_FALSE; + glGetProgramiv(program_ids[which], GL_LINK_STATUS, &ret); + if (ret != GL_TRUE) { + GLsizei len; + glGetProgramInfoLog(program_ids[which], sizeof(glbuf), &len, glbuf); + fprintf(stderr, "Failed to compile GLSL shader!\n%s", glbuf); + PyErr_SetString(PyExc_ValueError, "Failed to compile shader"); + goto end; + } + +end: + if (vertex_shader_id != 0) glDeleteShader(vertex_shader_id); + if (fragment_shader_id != 0) glDeleteShader(fragment_shader_id); + if (PyErr_Occurred()) { glDeleteProgram(program_ids[which]); program_ids[which] = 0; return NULL;} + return Py_BuildValue("I", program_ids[which]); + Py_RETURN_NONE; +} + +#define M(name, arg_type) {#name, (PyCFunction)name, arg_type, ""} static PyMethodDef module_methods[] = { - {"enable_automatic_opengl_error_checking", (PyCFunction)enable_automatic_error_checking, METH_O, NULL}, + M(enable_automatic_opengl_error_checking, METH_O), {"glewInit", (PyCFunction)glew_init, METH_NOARGS, NULL}, + M(compile_program, METH_VARARGS), + {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/kitty/shaders.py b/kitty/shaders.py index 9f1997130..f2c62a974 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -10,24 +10,20 @@ from ctypes import addressof, c_char, sizeof from functools import lru_cache from .fast_data_types import ( - GL_ARRAY_BUFFER, GL_CLAMP_TO_EDGE, GL_COMPILE_STATUS, GL_FLOAT, - GL_FRAGMENT_SHADER, GL_LINK_STATUS, GL_MAX_ARRAY_TEXTURE_LAYERS, + GL_ARRAY_BUFFER, GL_CLAMP_TO_EDGE, GL_FLOAT, GL_MAX_ARRAY_TEXTURE_LAYERS, GL_MAX_TEXTURE_SIZE, GL_NEAREST, GL_R8, GL_RED, GL_STREAM_DRAW, GL_TEXTURE0, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, - GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_TRUE, - GL_UNIFORM_BUFFER, GL_UNPACK_ALIGNMENT, GL_UNSIGNED_BYTE, GL_VERTEX_SHADER, - GL_WRITE_ONLY, GLSL_VERSION, copy_image_sub_data, + GL_TEXTURE_MIN_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, + GL_UNIFORM_BUFFER, GL_UNPACK_ALIGNMENT, GL_UNSIGNED_BYTE, GL_WRITE_ONLY, + GLSL_VERSION, compile_program, copy_image_sub_data, get_uniform_block_offsets, get_uniform_block_size, glActiveTexture, - glAttachShader, glBindBuffer, glBindBufferBase, glBindTexture, - glBindVertexArray, glCompileShader, glCopyImageSubData, glCreateProgram, - glCreateShader, glDeleteBuffer, glDeleteProgram, glDeleteShader, - glDeleteTexture, glDeleteVertexArray, glEnableVertexAttribArray, - glGenBuffers, glGenTextures, glGenVertexArrays, glGetAttribLocation, - glGetBufferSubData, glGetIntegerv, glGetProgramInfoLog, glGetProgramiv, - glGetShaderInfoLog, glGetShaderiv, glGetUniformBlockIndex, - glGetUniformLocation, glLinkProgram, glMapBuffer, glPixelStorei, - glShaderSource, glTexParameteri, glTexStorage3D, glTexSubImage3D, - glUnmapBuffer, glUseProgram, glVertexAttribDivisor, glVertexAttribPointer, + glBindBuffer, glBindBufferBase, glBindTexture, glBindVertexArray, + glCopyImageSubData, glDeleteBuffer, glDeleteTexture, glDeleteVertexArray, + glEnableVertexAttribArray, glGenBuffers, glGenTextures, glGenVertexArrays, + glGetAttribLocation, glGetBufferSubData, glGetIntegerv, + glGetUniformBlockIndex, glGetUniformLocation, glMapBuffer, glPixelStorei, + glTexParameteri, glTexStorage3D, glTexSubImage3D, glUnmapBuffer, + glUseProgram, glVertexAttribDivisor, glVertexAttribPointer, render_dirty_sprites, replace_or_create_buffer, sprite_map_current_layout, sprite_map_free, sprite_map_increment, sprite_map_set_layout, sprite_map_set_limits @@ -243,27 +239,16 @@ class Sprites: # {{{ class ShaderProgram: # {{{ """ Helper class for using GLSL shader programs """ - def __init__(self, vertex, fragment): + def __init__(self, which, vertex, fragment): """ Create a shader program. """ - self.program_id = glCreateProgram() - vs_id = self.add_shader(vertex, GL_VERTEX_SHADER) - glAttachShader(self.program_id, vs_id) - - frag_id = self.add_shader(fragment, GL_FRAGMENT_SHADER) - glAttachShader(self.program_id, frag_id) - - glLinkProgram(self.program_id) - if glGetProgramiv(self.program_id, GL_LINK_STATUS) != GL_TRUE: - info = glGetProgramInfoLog(self.program_id) - glDeleteProgram(self.program_id) - glDeleteShader(vs_id) - glDeleteShader(frag_id) - raise ValueError('Error linking shader program: \n%s' % info.decode('utf-8')) - glDeleteShader(vs_id) - glDeleteShader(frag_id) + self.program_id = compile_program( + which, + vertex.replace('GLSL_VERSION', str(GLSL_VERSION), 1), + fragment.replace('GLSL_VERSION', str(GLSL_VERSION), 1) + ) self.vertex_arrays = {} @contextmanager @@ -329,21 +314,6 @@ class ShaderProgram: # {{{ def __ne__(self, other) -> bool: return not self.__eq__(other) - def add_shader(self, source: str, shader_type: int) -> int: - ' Compile a shader and return its id, or raise an exception if compilation fails ' - shader_id = glCreateShader(shader_type) - source = source.replace('GLSL_VERSION', str(GLSL_VERSION), 1) - try: - glShaderSource(shader_id, source) - glCompileShader(shader_id) - if glGetShaderiv(shader_id, GL_COMPILE_STATUS) != GL_TRUE: - info = glGetShaderInfoLog(shader_id) - raise ValueError('GLSL {} compilation failed: \n{}'.format(shader_type, info.decode('utf-8'))) - return shader_id - except Exception: - glDeleteShader(shader_id) - raise - @lru_cache(maxsize=2**6) def uniform_location(self, name: str) -> int: ' Return the id for the uniform variable `name` or -1 if not found. '