From 06b42a7e1c4f412f6bb7064a678225bfb56f5dab Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 3 Nov 2016 10:30:35 +0530 Subject: [PATCH] More OpenGL bindings --- kitty/data-types.c | 1 + kitty/develop_gl.py | 37 ++++++++------ kitty/gl.h | 117 ++++++++++++++++++++++++++++++++++++++++++++ kitty/main.py | 5 +- kitty/shaders.py | 13 ----- 5 files changed, 142 insertions(+), 31 deletions(-) diff --git a/kitty/data-types.c b/kitty/data-types.c index 24f4b0ff3..fbbd1e62e 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -36,6 +36,7 @@ PyInit_fast_data_types(void) { if (!init_LineBuf(m)) return NULL; if (!init_Line(m)) return NULL; if (!init_Cursor(m)) return NULL; + if (!add_module_gl_constants(m)) return NULL; } return m; diff --git a/kitty/develop_gl.py b/kitty/develop_gl.py index a0a542575..53b8606f5 100644 --- a/kitty/develop_gl.py +++ b/kitty/develop_gl.py @@ -6,10 +6,15 @@ import glfw import OpenGL.GL as gl import sys -from kitty.shaders import ShaderProgram, GL_VERSION, Sprites, check_for_required_extensions +from kitty.shaders import ShaderProgram, GL_VERSION, Sprites from kitty.fonts import set_font_family, cell_size from kitty.char_grid import calculate_vertices, cell_shader -from kitty.fast_data_types import glViewport, enable_automatic_opengl_error_checking +from kitty.fast_data_types import ( + glViewport, enable_automatic_opengl_error_checking, glClearColor, + glUniform2f, glUniform4f, glUniform2ui, glUniform1i, glewInit, glGetString, + GL_VERSION as GL_VERSION_C, GL_VENDOR, GL_SHADING_LANGUAGE_VERSION, GL_RENDERER, + glClear, GL_COLOR_BUFFER_BIT, GL_TRIANGLE_FAN, glDrawArraysInstanced +) def rectangle_uv(left=0, top=0, right=1, bottom=1): @@ -61,13 +66,13 @@ class Renderer: with self.program: ul = self.program.uniform_location sg = self.screen_geometry - gl.glUniform2ui(ul('dimensions'), sg.xnum, sg.ynum) - gl.glUniform4f(ul('steps'), sg.xstart, sg.ystart, sg.dx, sg.dy) - gl.glUniform1i(ul('sprites'), self.sprites.sampler_num) - gl.glUniform1i(ul('sprite_map'), self.sprites.buffer_sampler_num) - gl.glUniform2f(ul('sprite_layout'), *self.sprites.layout) + glUniform2ui(ul('dimensions'), sg.xnum, sg.ynum) + glUniform4f(ul('steps'), sg.xstart, sg.ystart, sg.dx, sg.dy) + glUniform1i(ul('sprites'), self.sprites.sampler_num) + glUniform1i(ul('sprite_map'), self.sprites.buffer_sampler_num) + glUniform2f(ul('sprite_layout'), *self.sprites.layout) with self.sprites: - gl.glDrawArraysInstanced(gl.GL_TRIANGLE_FAN, 0, 4, sg.xnum * sg.ynum) + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, sg.xnum * sg.ynum) # window setup {{{ @@ -78,7 +83,7 @@ def key_callback(key, action): def gl_get_unicode(k): - ans = gl.glGetString(k) + ans = glGetString(k) if isinstance(ans, bytes): try: ans = ans.decode('utf-8') @@ -102,22 +107,22 @@ def _main(): if not window: raise SystemExit("glfwCreateWindow failed") glfw.glfwMakeContextCurrent(window) + glewInit() glfw.glfwSwapInterval(1) - check_for_required_extensions() # If everything went well the following calls # will display the version of opengl being used - print('Vendor: %s' % (gl_get_unicode(gl.GL_VENDOR))) - print('Opengl version: %s' % (gl_get_unicode(gl.GL_VERSION))) - print('GLSL Version: %s' % (gl_get_unicode(gl.GL_SHADING_LANGUAGE_VERSION))) - print('Renderer: %s' % (gl_get_unicode(gl.GL_RENDERER))) + print('Vendor: %s' % (gl_get_unicode(GL_VENDOR))) + print('Opengl version: %s' % (gl_get_unicode(GL_VERSION_C))) + print('GLSL Version: %s' % (gl_get_unicode(GL_SHADING_LANGUAGE_VERSION))) + print('Renderer: %s' % (gl_get_unicode(GL_RENDERER))) r = Renderer(1024, 1024) glfw.glfwSetFramebufferSizeCallback(window, r.on_resize) try: - gl.glClearColor(0.5, 0.5, 0.5, 0) + glClearColor(0.5, 0.5, 0.5, 0) while not glfw.glfwWindowShouldClose(window): - gl.glClear(gl.GL_COLOR_BUFFER_BIT) + glClear(GL_COLOR_BUFFER_BIT) r.render() glfw.glfwSwapBuffers(window) glfw.glfwWaitEvents() diff --git a/kitty/gl.h b/kitty/gl.h index 55aee9fed..38cb7865d 100644 --- a/kitty/gl.h +++ b/kitty/gl.h @@ -51,13 +51,130 @@ Viewport(PyObject UNUSED *self, PyObject *args) { Py_RETURN_NONE; } +static PyObject* +ClearColor(PyObject UNUSED *self, PyObject *args) { + float x, y, w, h; + if (!PyArg_ParseTuple(args, "ffff", &x, &y, &w, &h)) return NULL; + glClearColor(x, y, w, h); + CHECK_ERROR; + Py_RETURN_NONE; +} + +// Uniforms {{{ +static PyObject* +Uniform2ui(PyObject UNUSED *self, PyObject *args) { + int location; + unsigned int x, y; + if (!PyArg_ParseTuple(args, "iII", &location, &x, &y)) return NULL; + glUniform2ui(location, x, y); + CHECK_ERROR; + Py_RETURN_NONE; +} + +static PyObject* +Uniform1i(PyObject UNUSED *self, PyObject *args) { + int location; + int x; + if (!PyArg_ParseTuple(args, "ii", &location, &x)) return NULL; + glUniform1i(location, x); + CHECK_ERROR; + Py_RETURN_NONE; +} + + +static PyObject* +Uniform2f(PyObject UNUSED *self, PyObject *args) { + int location; + float x, y; + if (!PyArg_ParseTuple(args, "iff", &location, &x, &y)) return NULL; + glUniform2f(location, x, y); + CHECK_ERROR; + Py_RETURN_NONE; +} + +static PyObject* +Uniform4f(PyObject UNUSED *self, PyObject *args) { + int location; + float x, y, a, b; + if (!PyArg_ParseTuple(args, "iffff", &location, &x, &y, &a, &b)) return NULL; + glUniform4f(location, x, y, a, b); + CHECK_ERROR; + Py_RETURN_NONE; +} +// }}} + + static PyObject* CheckError(PyObject UNUSED *self) { CHECK_ERROR; Py_RETURN_NONE; } +static PyObject* +_glewInit(PyObject UNUSED *self) { + GLenum err = glewInit(); + if (err != GLEW_OK) { + PyErr_Format(PyExc_RuntimeError, "GLEW init failed: %s", glewGetErrorString(err)); + return NULL; + } + if(!GLEW_ARB_copy_image) { + PyErr_SetString(PyExc_RuntimeError, "OpenGL is missing the required ARB_copy_image extension"); + return NULL; + } + if(!GLEW_ARB_texture_storage) { + PyErr_SetString(PyExc_RuntimeError, "OpenGL is missing the required ARB_texture_storage extension"); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject* +GetString(PyObject UNUSED *self, PyObject *val) { + const unsigned char *ans = glGetString(PyLong_AsUnsignedLong(val)); + if (ans == NULL) { SET_GL_ERR; return NULL; } + return PyBytes_FromString((const char*)ans); +} + +static PyObject* +Clear(PyObject UNUSED *self, PyObject *val) { + unsigned long m = PyLong_AsUnsignedLong(val); + glClear((GLbitfield)m); + CHECK_ERROR; + Py_RETURN_NONE; +} + +static PyObject* +DrawArraysInstanced(PyObject UNUSED *self, PyObject *args) { + int mode, first; + unsigned int count, primcount; + if (!PyArg_ParseTuple(args, "iiII", &mode, &first, &count, &primcount)) return NULL; + glDrawArraysInstanced(mode, first, count, primcount); + CHECK_ERROR; + Py_RETURN_NONE; +} + +int add_module_gl_constants(PyObject *module) { +#define GLC(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return 0; } + GLC(GL_VERSION); + GLC(GL_VENDOR); + GLC(GL_SHADING_LANGUAGE_VERSION); + GLC(GL_RENDERER); + GLC(GL_TRIANGLE_FAN); + GLC(GL_COLOR_BUFFER_BIT); + return 1; +} + #define GL_METHODS \ {"enable_automatic_opengl_error_checking", (PyCFunction)enable_automatic_error_checking, METH_O, NULL}, \ + {"glewInit", (PyCFunction)_glewInit, METH_NOARGS, NULL}, \ METH(Viewport, METH_VARARGS) \ METH(CheckError, METH_NOARGS) \ + METH(ClearColor, METH_VARARGS) \ + METH(Uniform2ui, METH_VARARGS) \ + METH(Uniform1i, METH_VARARGS) \ + METH(Uniform2f, METH_VARARGS) \ + METH(Uniform4f, METH_VARARGS) \ + METH(GetString, METH_O) \ + METH(Clear, METH_O) \ + METH(DrawArraysInstanced, METH_VARARGS) \ + diff --git a/kitty/main.py b/kitty/main.py index a5957a6b3..c30e6a577 100644 --- a/kitty/main.py +++ b/kitty/main.py @@ -14,7 +14,8 @@ from .config import load_config from .constants import appname, str_version, config_dir from .boss import Boss from .utils import fork_child, hangup -from .shaders import GL_VERSION, check_for_required_extensions +from .shaders import GL_VERSION +from .fast_data_types import glewInit import glfw @@ -53,7 +54,7 @@ def run_app(opts, args): glfw.glfwSetWindowTitle(window, appname.encode('utf-8')) try: glfw.glfwMakeContextCurrent(window) - check_for_required_extensions() + glewInit() glfw.glfwSwapInterval(1) boss = Boss(window, window_width, window_height, opts, args) glfw.glfwSetFramebufferSizeCallback(window, boss.on_window_resize) diff --git a/kitty/shaders.py b/kitty/shaders.py index 686016775..e579c50bd 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -14,25 +14,12 @@ from .data_types import ITALIC_MASK, BOLD_MASK GL_VERSION = (3, 3) VERSION = GL_VERSION[0] * 100 + GL_VERSION[1] * 10 -REQUIRED_EXTENSIONS = frozenset('GL_ARB_copy_image GL_ARB_texture_storage'.split()) def array(*args, dtype=gl.GLfloat): return (dtype * len(args))(*args) -def check_for_required_extensions(): - num = gl.glGetIntegerv(gl.GL_NUM_EXTENSIONS) - required = set(REQUIRED_EXTENSIONS) - for i in range(num): - ext = gl.glGetStringi(gl.GL_EXTENSIONS, i).decode('utf-8') - required.discard(ext) - if not required: - break - if required: - raise RuntimeError('Your OpenGL implementation is missing the following required extensions: %s' % ','.join(required)) - - class Sprites: ''' Maintain sprite sheets of all rendered characters on the GPU as a texture array with each texture being a sprite sheet. '''