diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 46c90617f..86d91e0cf 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -29,7 +29,6 @@ extern int pthread_setname_np(const char *name); #include #define EXTRA_FDS 2 -#define MAX_CHILDREN 256 static void (*parse_func)(Screen*, PyObject*); diff --git a/kitty/data-types.h b/kitty/data-types.h index cc44effc1..c55fd9db6 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -38,6 +38,7 @@ typedef uint16_t sprite_index; #define SGR_PROTOCOL 2 #define URXVT_PROTOCOL 3 +#define MAX_CHILDREN 256 #define BLANK_CHAR 0 #define CHAR_MASK 0xFFFFFF #define ATTRS_SHIFT 24 diff --git a/kitty/gl.c b/kitty/gl.c index 6709ee960..2ce4c20b7 100644 --- a/kitty/gl.c +++ b/kitty/gl.c @@ -853,32 +853,6 @@ static PyMethodDef module_methods[] = { bool add_module_gl_constants(PyObject *module) { -#define GLC(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return false; } - GLC(GL_VERSION); - GLC(GL_VENDOR); - GLC(GL_SHADING_LANGUAGE_VERSION); - GLC(GL_RENDERER); - GLC(GL_TRIANGLE_FAN); GLC(GL_TRIANGLE_STRIP); GLC(GL_TRIANGLES); GLC(GL_LINE_LOOP); - GLC(GL_COLOR_BUFFER_BIT); - GLC(GL_VERTEX_SHADER); - GLC(GL_FRAGMENT_SHADER); - GLC(GL_TRUE); - GLC(GL_FALSE); - GLC(GL_COMPILE_STATUS); - GLC(GL_LINK_STATUS); - GLC(GL_TEXTURE0); GLC(GL_TEXTURE1); GLC(GL_TEXTURE2); GLC(GL_TEXTURE3); GLC(GL_TEXTURE4); GLC(GL_TEXTURE5); GLC(GL_TEXTURE6); GLC(GL_TEXTURE7); GLC(GL_TEXTURE8); - GLC(GL_MAX_ARRAY_TEXTURE_LAYERS); GLC(GL_TEXTURE_BINDING_BUFFER); GLC(GL_MAX_TEXTURE_BUFFER_SIZE); - GLC(GL_MAX_TEXTURE_SIZE); - GLC(GL_TEXTURE_2D_ARRAY); - GLC(GL_LINEAR); GLC(GL_CLAMP_TO_EDGE); GLC(GL_NEAREST); - GLC(GL_TEXTURE_MIN_FILTER); GLC(GL_TEXTURE_MAG_FILTER); - GLC(GL_TEXTURE_WRAP_S); GLC(GL_TEXTURE_WRAP_T); - GLC(GL_UNPACK_ALIGNMENT); - GLC(GL_R8); GLC(GL_RED); GLC(GL_UNSIGNED_BYTE); GLC(GL_UNSIGNED_SHORT); GLC(GL_R32UI); GLC(GL_RGB32UI); GLC(GL_RGBA); - GLC(GL_TEXTURE_BUFFER); GLC(GL_STATIC_DRAW); GLC(GL_STREAM_DRAW); GLC(GL_DYNAMIC_DRAW); - GLC(GL_SRC_ALPHA); GLC(GL_ONE_MINUS_SRC_ALPHA); - GLC(GL_WRITE_ONLY); GLC(GL_READ_ONLY); GLC(GL_READ_WRITE); - GLC(GL_BLEND); GLC(GL_FLOAT); GLC(GL_UNSIGNED_INT); GLC(GL_ARRAY_BUFFER); GLC(GL_UNIFORM_BUFFER); if (PyModule_AddFunctions(module, module_methods) != 0) return false; return true; } diff --git a/kitty/shaders.c b/kitty/shaders.c index 0cd6c2d77..7d6aeaa1b 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -29,33 +29,59 @@ static char glbuf[4096]; #define GL_STACK_OVERFLOW 0x0503 #endif +static int gl_error = GL_NO_ERROR; +static const char *local_error = NULL; + static inline bool set_error_from_gl() { - int code = glGetError(); - switch(code) { - case GL_NO_ERROR: return false; - case GL_INVALID_ENUM: - PyErr_SetString(PyExc_ValueError, "An enum value is invalid (GL_INVALID_ENUM)"); break; - case GL_INVALID_VALUE: - PyErr_SetString(PyExc_ValueError, "An numeric value is invalid (GL_INVALID_VALUE)"); break; - case GL_INVALID_OPERATION: - PyErr_SetString(PyExc_ValueError, "This operation is not allowed in the current state (GL_INVALID_OPERATION)"); break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - PyErr_SetString(PyExc_ValueError, "The framebuffer object is not complete (GL_INVALID_FRAMEBUFFER_OPERATION)"); break; - case GL_OUT_OF_MEMORY: - PyErr_SetString(PyExc_MemoryError, "There is not enough memory left to execute the command. (GL_OUT_OF_MEMORY)"); break; - case GL_STACK_UNDERFLOW: - PyErr_SetString(PyExc_OverflowError, "An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_UNDERFLOW)"); break; - case GL_STACK_OVERFLOW: - PyErr_SetString(PyExc_OverflowError, "An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_OVERFLOW)"); break; - default: - PyErr_Format(PyExc_RuntimeError, "An unknown OpenGL error occurred with code: %d", code); break; + if (gl_error != GL_NO_ERROR) return true; + gl_error = glGetError(); + return gl_error == GL_NO_ERROR ? false : true; +} + + +static inline void +set_local_error_(const char* msg, int line_no) { + static char buf[256] = {0}; + if (!local_error) { + snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s (line: %d)", msg, line_no); + local_error = buf; } - return true; +} +#define set_local_error(msg) set_local_error_(msg, __LINE__) + +static const char* +gl_strerror(int code) { + static char buf[256] = {0}; + const char *ans = NULL; + if (local_error) { ans = local_error; local_error = NULL; } + else { + switch(code) { + case GL_NO_ERROR: break; + case GL_INVALID_ENUM: + ans = "An enum value is invalid (GL_INVALID_ENUM)"; break; + case GL_INVALID_VALUE: + ans = "An numeric value is invalid (GL_INVALID_VALUE)"; break; + case GL_INVALID_OPERATION: + ans = "This operation is not allowed in the current state (GL_INVALID_OPERATION)"; break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + ans = "The framebuffer object is not complete (GL_INVALID_FRAMEBUFFER_OPERATION)"; break; + case GL_OUT_OF_MEMORY: + ans = "There is not enough memory left to execute the command. (GL_OUT_OF_MEMORY)"; break; + case GL_STACK_UNDERFLOW: + ans = "An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_UNDERFLOW)"; break; + case GL_STACK_OVERFLOW: + ans = "An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_OVERFLOW)"; break; + default: + snprintf(buf, sizeof(buf)/sizeof(buf[0]), "An unknown OpenGL error occurred with code: %d", code); break; + ans = buf; + } + } + gl_error = GL_NO_ERROR; + return ans; } static bool _enable_error_checking = false; -#define CHECK_ERROR if (_enable_error_checking) { if (set_error_from_gl()) return NULL; } static PyObject* @@ -103,8 +129,171 @@ compile_shader(GLenum shader_type, const char *source) { } return shader_id; } + +static inline GLint +get_attrib_location(int program, const char *name) { + return glGetAttribLocation(program_ids[program], name); +} + +static void +bind_program(int program) { + glUseProgram(program_ids[program]); +} + +static void +unbind_program() { + glUseProgram(0); +} // }}} +// Buffers {{{ + +typedef struct { + GLuint id; + GLsizeiptr size; + GLenum usage; +} Buffer; + + +static Buffer buffers[MAX_CHILDREN * 4 + 4] = {{0}}; + +static ssize_t +create_buffer(GLenum usage) { + GLuint buffer_id; + glGenBuffers(1, &buffer_id); + if (set_error_from_gl()) return -1; + for (size_t i = 0; i < sizeof(buffers)/sizeof(buffers[0]); i++) { + if (!buffers[i].id) { + buffers[i].id = buffer_id; + buffers[i].size = 0; + buffers[i].usage = usage; + } + } + glDeleteBuffers(1, &buffer_id); + set_local_error("too many buffers"); + return -1; +} + +static void +delete_buffer(ssize_t buf_idx) { + glDeleteBuffers(1, &(buffers[buf_idx].id)); + buffers[buf_idx].id = 0; + buffers[buf_idx].size = 0; +} + +static bool +bind_buffer(ssize_t buf_idx) { + glBindBuffer(buffers[buf_idx].usage, buffers[buf_idx].id); + if (set_error_from_gl()) return false; + return true; +} + +static bool +unbind_buffer(ssize_t buf_idx) { + glBindBuffer(buffers[buf_idx].usage, 0); + if (set_error_from_gl()) return false; + return true; +} + +// }}} + +// Vertex Array Objects (VAO) {{{ + +typedef struct { + GLuint id; + size_t num_buffers; + ssize_t buffers[10]; +} VAO; + +static VAO vaos[MAX_CHILDREN + 10] = {{0}}; + +static ssize_t +create_vao() { + GLuint vao_id; + glGenVertexArrays(1, &vao_id); + if (set_error_from_gl()) return -1; + for (size_t i = 0; i < sizeof(vaos)/sizeof(vaos[0]); i++) { + if (!vaos[i].id) { + vaos[i].id = vao_id; + vaos[i].num_buffers = 0; + glBindVertexArray(vao_id); + if (set_error_from_gl()) return -1; + return i; + } + } + glDeleteVertexArrays(1, &vao_id); + set_local_error("too many VAOs"); + return -1; +} + +static bool +add_buffer_to_vao(ssize_t vao_idx, GLenum usage) { + VAO* vao = vaos + vao_idx; + if (vao->num_buffers >= sizeof(vao->buffers) / sizeof(vao->buffers[0])) { + set_local_error("too many buffers in a single VAO"); + return false; + } + ssize_t buf = create_buffer(usage); + if (buf < 0) return false; + vao->buffers[vao->num_buffers++] = buf; + return true; +} + +static bool +add_attribute_to_vao(int p, ssize_t vao_idx, const char *name, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) { + VAO *vao = vaos + vao_idx; + static char err[256] = {0}; + if (!vao->num_buffers) { set_local_error("You must create a buffer for this attribute first"); return false; } + GLint attrib_location = get_attrib_location(p, name); + if (set_error_from_gl()) return false; + if (attrib_location == -1) { snprintf(err, sizeof(err)/sizeof(err[0]), "No attribute named: %s found in this program", name); set_local_error(err); return false; } + ssize_t buf = vao->buffers[vao->num_buffers - 1]; + if (!bind_buffer(buf)) return false; + glEnableVertexAttribArray(attrib_location); + if (set_error_from_gl()) return false; + switch(data_type) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_INT: + case GL_UNSIGNED_INT: + glVertexAttribIPointer(attrib_location, size, data_type, stride, offset); + break; + default: + glVertexAttribPointer(attrib_location, size, data_type, GL_FALSE, stride, offset); + break; + } + if (set_error_from_gl()) return false; + if (divisor) { + glVertexAttribDivisor(attrib_location, divisor); + if (set_error_from_gl()) return false; + } + unbind_buffer(buf); + return true; +} + +static void +remove_vao(ssize_t vao_idx) { + VAO *vao = vaos + vao_idx; + while (vao->num_buffers) { + vao->num_buffers--; + delete_buffer(vao->buffers[vao->num_buffers]); + } + glDeleteVertexArrays(1, &(vao->id)); + vaos[vao_idx].id = 0; +} + +static void +bind_vertex_array(ssize_t vao_idx) { + glBindVertexArray(vaos[vao_idx].id); +} + +static void +unbind_vertex_array() { + glBindVertexArray(0); +} +// }}} // Python API {{{ static PyObject* @@ -113,6 +302,14 @@ enable_automatic_opengl_error_checking(PyObject UNUSED *self, PyObject *val) { Py_RETURN_NONE; } +static inline bool +translate_error() { + if (PyErr_Occurred()) return true; + const char *m = gl_strerror(gl_error); + if (m != NULL) { PyErr_SetString(PyExc_ValueError, m); return true; } + return false; +} + static PyObject* compile_program(PyObject UNUSED *self, PyObject *args) { const char *vertex_shader, *fragment_shader; @@ -144,16 +341,66 @@ compile_program(PyObject UNUSED *self, PyObject *args) { end: if (vertex_shader_id != 0) glDeleteShader(vertex_shader_id); if (fragment_shader_id != 0) glDeleteShader(fragment_shader_id); + translate_error(); if (PyErr_Occurred()) { glDeleteProgram(program_ids[which]); program_ids[which] = 0; return NULL;} return Py_BuildValue("I", program_ids[which]); Py_RETURN_NONE; } +#define CHECK_ERROR if (_enable_error_checking) { translate_error(); if (PyErr_Occurred()) return NULL; } +#define PYWRAP0(name) static PyObject* py##name(PyObject UNUSED *self) +#define PYWRAP1(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args) +#define PYWRAP2(name) static PyObject* py##name(PyObject UNUSED *self, PyObject *args, PyObject *kw) +#define PA(fmt, ...) if(!PyArg_ParseTuple(args, fmt, __VA_ARGS__)) return NULL; +#define ONE_INT(name) PYWRAP1(name) { name(PyLong_AsSsize_t(args)); CHECK_ERROR; Py_RETURN_NONE; } +#define NO_ARG(name) PYWRAP0(name) { name(); CHECK_ERROR; Py_RETURN_NONE; } + +ONE_INT(bind_program) +NO_ARG(unbind_program) + +PYWRAP0(create_vao) { + int ans = create_vao(); + if (ans < 0) return NULL; + return Py_BuildValue("i", ans); +} + +ONE_INT(remove_vao) + +PYWRAP1(add_buffer_to_vao) { + int vao_idx, usage; + PA("ii", &vao_idx, &usage); + if (!add_buffer_to_vao(vao_idx, usage)) return NULL; + Py_RETURN_NONE; +} + +PYWRAP2(add_attribute_to_vao) { + int program, vao, data_type = GL_FLOAT, size = 3; + char *name; + unsigned int stride = 0, divisor = 0; + PyObject *offset; + static char* keywords[] = {"program", "vao", "name", "size", "dtype", "stride", "offset", "divisor", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, kw, "i i s | i i I O! I", keywords, &program, &vao, &name, &size, &data_type, &stride, &offset, &PyLong_Type, &divisor)) return NULL; + if (!add_attribute_to_vao(program, vao, name, size, data_type, stride, PyLong_AsVoidPtr(offset), divisor)) return NULL; + Py_RETURN_NONE; +} + +ONE_INT(bind_vertex_array) +NO_ARG(unbind_vertex_array) + #define M(name, arg_type) {#name, (PyCFunction)name, arg_type, NULL} +#define MW(name, arg_type) {#name, (PyCFunction)py##name, arg_type, NULL} static PyMethodDef module_methods[] = { M(enable_automatic_opengl_error_checking, METH_O), {"glewInit", (PyCFunction)glew_init, METH_NOARGS, NULL}, M(compile_program, METH_VARARGS), + MW(create_vao, METH_NOARGS), + MW(remove_vao, METH_O), + MW(add_buffer_to_vao, METH_VARARGS), + MW(add_attribute_to_vao, METH_VARARGS), + MW(bind_vertex_array, METH_O), + MW(unbind_vertex_array, METH_NOARGS), + MW(bind_program, METH_O), + MW(unbind_program, METH_NOARGS), {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -163,6 +410,32 @@ init_shaders(PyObject *module) { #define C(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return false; } C(CELL_PROGRAM); C(CURSOR_PROGRAM); C(BORDERS_PROGRAM); C(GLSL_VERSION); + C(GL_VERSION); + C(GL_VENDOR); + C(GL_SHADING_LANGUAGE_VERSION); + C(GL_RENDERER); + C(GL_TRIANGLE_FAN); C(GL_TRIANGLE_STRIP); C(GL_TRIANGLES); C(GL_LINE_LOOP); + C(GL_COLOR_BUFFER_BIT); + C(GL_VERTEX_SHADER); + C(GL_FRAGMENT_SHADER); + C(GL_TRUE); + C(GL_FALSE); + C(GL_COMPILE_STATUS); + C(GL_LINK_STATUS); + C(GL_TEXTURE0); C(GL_TEXTURE1); C(GL_TEXTURE2); C(GL_TEXTURE3); C(GL_TEXTURE4); C(GL_TEXTURE5); C(GL_TEXTURE6); C(GL_TEXTURE7); C(GL_TEXTURE8); + C(GL_MAX_ARRAY_TEXTURE_LAYERS); C(GL_TEXTURE_BINDING_BUFFER); C(GL_MAX_TEXTURE_BUFFER_SIZE); + C(GL_MAX_TEXTURE_SIZE); + C(GL_TEXTURE_2D_ARRAY); + C(GL_LINEAR); C(GL_CLAMP_TO_EDGE); C(GL_NEAREST); + C(GL_TEXTURE_MIN_FILTER); C(GL_TEXTURE_MAG_FILTER); + C(GL_TEXTURE_WRAP_S); C(GL_TEXTURE_WRAP_T); + C(GL_UNPACK_ALIGNMENT); + C(GL_R8); C(GL_RED); C(GL_UNSIGNED_BYTE); C(GL_UNSIGNED_SHORT); C(GL_R32UI); C(GL_RGB32UI); C(GL_RGBA); + C(GL_TEXTURE_BUFFER); C(GL_STATIC_DRAW); C(GL_STREAM_DRAW); C(GL_DYNAMIC_DRAW); + C(GL_SRC_ALPHA); C(GL_ONE_MINUS_SRC_ALPHA); + C(GL_WRITE_ONLY); C(GL_READ_ONLY); C(GL_READ_WRITE); + C(GL_BLEND); C(GL_FLOAT); C(GL_UNSIGNED_INT); C(GL_ARRAY_BUFFER); C(GL_UNIFORM_BUFFER); + #undef C PyModule_AddObject(module, "GL_VERSION_REQUIRED", Py_BuildValue("II", REQUIRED_VERSION_MAJOR, REQUIRED_VERSION_MINOR)); if (PyModule_AddFunctions(module, module_methods) != 0) return false;