mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-19 23:14:55 +02:00
OpenGL bindings for programs
This commit is contained in:
194
kitty/gl.h
194
kitty/gl.h
@@ -152,6 +152,175 @@ DrawArraysInstanced(PyObject UNUSED *self, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
CreateProgram(PyObject UNUSED *self) {
|
||||||
|
GLuint ans = glCreateProgram();
|
||||||
|
if (!ans) { SET_GL_ERR; return NULL; }
|
||||||
|
return PyLong_FromUnsignedLong(ans);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
AttachShader(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
unsigned int program_id, shader_id;
|
||||||
|
if (!PyArg_ParseTuple(args, "II", &program_id, &shader_id)) return NULL;
|
||||||
|
glAttachShader(program_id, shader_id);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
LinkProgram(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
unsigned long program_id = PyLong_AsUnsignedLong(val);
|
||||||
|
glLinkProgram(program_id);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
GetProgramiv(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
unsigned int program_id;
|
||||||
|
int pname, ans = 0;
|
||||||
|
if (!PyArg_ParseTuple(args, "Ii", &program_id, &pname)) return NULL;
|
||||||
|
glGetProgramiv(program_id, pname, &ans);
|
||||||
|
CHECK_ERROR;
|
||||||
|
return PyLong_FromLong((long)ans);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
GetProgramInfoLog(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
unsigned long program_id = PyLong_AsUnsignedLong(val);
|
||||||
|
int log_len = 0;
|
||||||
|
GLchar *buf = NULL;
|
||||||
|
glGetProgramiv(program_id, GL_INFO_LOG_LENGTH, &log_len);
|
||||||
|
buf = PyMem_Calloc(log_len + 10, sizeof(GLchar));
|
||||||
|
if (buf == NULL) return PyErr_NoMemory();
|
||||||
|
glGetProgramInfoLog(program_id, log_len, &log_len, buf);
|
||||||
|
PyObject *ans = PyBytes_FromStringAndSize(buf, log_len);
|
||||||
|
PyMem_Free(buf);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
GetShaderInfoLog(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
unsigned long program_id = PyLong_AsUnsignedLong(val);
|
||||||
|
int log_len = 0;
|
||||||
|
GLchar *buf = NULL;
|
||||||
|
glGetShaderiv(program_id, GL_INFO_LOG_LENGTH, &log_len);
|
||||||
|
buf = PyMem_Calloc(log_len + 10, sizeof(GLchar));
|
||||||
|
if (buf == NULL) return PyErr_NoMemory();
|
||||||
|
glGetShaderInfoLog(program_id, log_len, &log_len, buf);
|
||||||
|
PyObject *ans = PyBytes_FromStringAndSize(buf, log_len);
|
||||||
|
PyMem_Free(buf);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
DeleteProgram(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
unsigned long program_id = PyLong_AsUnsignedLong(val);
|
||||||
|
glDeleteProgram(program_id);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
DeleteShader(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
unsigned long program_id = PyLong_AsUnsignedLong(val);
|
||||||
|
glDeleteShader(program_id);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
GenVertexArrays(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
GLuint arrays[256] = {0};
|
||||||
|
unsigned long n = PyLong_AsUnsignedLong(val);
|
||||||
|
if (n > 256) { PyErr_SetString(PyExc_ValueError, "Generating more than 256 arrays in a single call is not supported"); return NULL; }
|
||||||
|
glGenVertexArrays(n, arrays);
|
||||||
|
CHECK_ERROR;
|
||||||
|
if (n == 1) return PyLong_FromUnsignedLong((unsigned long)arrays[0]);
|
||||||
|
PyObject *ans = PyTuple_New(n);
|
||||||
|
if (ans == NULL) return PyErr_NoMemory();
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
PyObject *t = PyLong_FromUnsignedLong((unsigned long)arrays[i]);
|
||||||
|
if (t == NULL) { Py_DECREF(ans); return PyErr_NoMemory(); }
|
||||||
|
PyTuple_SET_ITEM(ans, i, t);
|
||||||
|
}
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
CreateShader(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
long shader_type = PyLong_AsLong(val);
|
||||||
|
GLuint ans = glCreateShader(shader_type);
|
||||||
|
CHECK_ERROR;
|
||||||
|
return PyLong_FromUnsignedLong(ans);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
ShaderSource(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
char *src; Py_ssize_t src_len = 0;
|
||||||
|
unsigned int shader_id;
|
||||||
|
if(!PyArg_ParseTuple(args, "Is#", &shader_id, &src, &src_len)) return NULL;
|
||||||
|
glShaderSource(shader_id, 1, (const GLchar * const*)&src, NULL);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
CompileShader(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
unsigned long shader_id = PyLong_AsUnsignedLong(val);
|
||||||
|
glCompileShader((GLuint)shader_id);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
GetShaderiv(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
unsigned int program_id;
|
||||||
|
int pname, ans = 0;
|
||||||
|
if (!PyArg_ParseTuple(args, "Ii", &program_id, &pname)) return NULL;
|
||||||
|
glGetShaderiv(program_id, pname, &ans);
|
||||||
|
CHECK_ERROR;
|
||||||
|
return PyLong_FromLong((long)ans);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
GetUniformLocation(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
char *name;
|
||||||
|
unsigned int program_id;
|
||||||
|
if(!PyArg_ParseTuple(args, "Is", &program_id, &name)) return NULL;
|
||||||
|
GLint ans = glGetUniformLocation(program_id, name);
|
||||||
|
CHECK_ERROR;
|
||||||
|
return PyLong_FromLong((long) ans);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
GetAttribLocation(PyObject UNUSED *self, PyObject *args) {
|
||||||
|
char *name;
|
||||||
|
unsigned int program_id;
|
||||||
|
if(!PyArg_ParseTuple(args, "Is", &program_id, &name)) return NULL;
|
||||||
|
GLint ans = glGetAttribLocation(program_id, name);
|
||||||
|
CHECK_ERROR;
|
||||||
|
return PyLong_FromLong((long) ans);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
UseProgram(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
unsigned long program_id = PyLong_AsUnsignedLong(val);
|
||||||
|
glUseProgram(program_id);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
BindVertexArray(PyObject UNUSED *self, PyObject *val) {
|
||||||
|
unsigned long program_id = PyLong_AsUnsignedLong(val);
|
||||||
|
glBindVertexArray(program_id);
|
||||||
|
CHECK_ERROR;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
int add_module_gl_constants(PyObject *module) {
|
int add_module_gl_constants(PyObject *module) {
|
||||||
#define GLC(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return 0; }
|
#define GLC(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return 0; }
|
||||||
GLC(GL_VERSION);
|
GLC(GL_VERSION);
|
||||||
@@ -160,21 +329,44 @@ int add_module_gl_constants(PyObject *module) {
|
|||||||
GLC(GL_RENDERER);
|
GLC(GL_RENDERER);
|
||||||
GLC(GL_TRIANGLE_FAN);
|
GLC(GL_TRIANGLE_FAN);
|
||||||
GLC(GL_COLOR_BUFFER_BIT);
|
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);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define GL_METHODS \
|
#define GL_METHODS \
|
||||||
{"enable_automatic_opengl_error_checking", (PyCFunction)enable_automatic_error_checking, METH_O, NULL}, \
|
{"enable_automatic_opengl_error_checking", (PyCFunction)enable_automatic_error_checking, METH_O, NULL}, \
|
||||||
{"glewInit", (PyCFunction)_glewInit, METH_NOARGS, NULL}, \
|
{"glewInit", (PyCFunction)_glewInit, METH_NOARGS, NULL}, \
|
||||||
METH(Viewport, METH_VARARGS) \
|
METH(Viewport, METH_VARARGS) \
|
||||||
METH(CheckError, METH_NOARGS) \
|
METH(CheckError, METH_NOARGS) \
|
||||||
METH(ClearColor, METH_VARARGS) \
|
METH(ClearColor, METH_VARARGS) \
|
||||||
|
METH(GetProgramiv, METH_VARARGS) \
|
||||||
|
METH(GetShaderiv, METH_VARARGS) \
|
||||||
METH(Uniform2ui, METH_VARARGS) \
|
METH(Uniform2ui, METH_VARARGS) \
|
||||||
METH(Uniform1i, METH_VARARGS) \
|
METH(Uniform1i, METH_VARARGS) \
|
||||||
METH(Uniform2f, METH_VARARGS) \
|
METH(Uniform2f, METH_VARARGS) \
|
||||||
METH(Uniform4f, METH_VARARGS) \
|
METH(Uniform4f, METH_VARARGS) \
|
||||||
|
METH(GetUniformLocation, METH_VARARGS) \
|
||||||
|
METH(GetAttribLocation, METH_VARARGS) \
|
||||||
|
METH(ShaderSource, METH_VARARGS) \
|
||||||
|
METH(CompileShader, METH_O) \
|
||||||
METH(GetString, METH_O) \
|
METH(GetString, METH_O) \
|
||||||
METH(Clear, METH_O) \
|
METH(Clear, METH_O) \
|
||||||
|
METH(CreateShader, METH_O) \
|
||||||
|
METH(GenVertexArrays, METH_O) \
|
||||||
|
METH(LinkProgram, METH_O) \
|
||||||
|
METH(UseProgram, METH_O) \
|
||||||
|
METH(BindVertexArray, METH_O) \
|
||||||
|
METH(DeleteProgram, METH_O) \
|
||||||
|
METH(DeleteShader, METH_O) \
|
||||||
|
METH(GetProgramInfoLog, METH_O) \
|
||||||
|
METH(GetShaderInfoLog, METH_O) \
|
||||||
METH(DrawArraysInstanced, METH_VARARGS) \
|
METH(DrawArraysInstanced, METH_VARARGS) \
|
||||||
|
METH(CreateProgram, METH_NOARGS) \
|
||||||
|
METH(AttachShader, METH_VARARGS) \
|
||||||
|
|
||||||
|
|||||||
@@ -11,15 +11,17 @@ from OpenGL.GL.ARB.texture_storage import glTexStorage3D # only present in open
|
|||||||
|
|
||||||
from .fonts import render_cell
|
from .fonts import render_cell
|
||||||
from .data_types import ITALIC_MASK, BOLD_MASK
|
from .data_types import ITALIC_MASK, BOLD_MASK
|
||||||
|
from .fast_data_types import (
|
||||||
|
glCreateProgram, glAttachShader, GL_FRAGMENT_SHADER, GL_VERTEX_SHADER, glLinkProgram,
|
||||||
|
GL_TRUE, GL_LINK_STATUS, glGetProgramiv, glGetProgramInfoLog, glDeleteShader, glDeleteProgram,
|
||||||
|
glGenVertexArrays, glCreateShader, glShaderSource, glCompileShader, glGetShaderiv, GL_COMPILE_STATUS,
|
||||||
|
glGetShaderInfoLog, glGetUniformLocation, glGetAttribLocation, glUseProgram, glBindVertexArray,
|
||||||
|
)
|
||||||
|
|
||||||
GL_VERSION = (3, 3)
|
GL_VERSION = (3, 3)
|
||||||
VERSION = GL_VERSION[0] * 100 + GL_VERSION[1] * 10
|
VERSION = GL_VERSION[0] * 100 + GL_VERSION[1] * 10
|
||||||
|
|
||||||
|
|
||||||
def array(*args, dtype=gl.GLfloat):
|
|
||||||
return (dtype * len(args))(*args)
|
|
||||||
|
|
||||||
|
|
||||||
class Sprites:
|
class Sprites:
|
||||||
''' Maintain sprite sheets of all rendered characters on the GPU as a texture
|
''' Maintain sprite sheets of all rendered characters on the GPU as a texture
|
||||||
array with each texture being a sprite sheet. '''
|
array with each texture being a sprite sheet. '''
|
||||||
@@ -163,24 +165,24 @@ class ShaderProgram:
|
|||||||
Create a shader program.
|
Create a shader program.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.program_id = gl.glCreateProgram()
|
self.program_id = glCreateProgram()
|
||||||
self.is_active = False
|
self.is_active = False
|
||||||
vs_id = self.add_shader(vertex, gl.GL_VERTEX_SHADER)
|
vs_id = self.add_shader(vertex, GL_VERTEX_SHADER)
|
||||||
gl.glAttachShader(self.program_id, vs_id)
|
glAttachShader(self.program_id, vs_id)
|
||||||
|
|
||||||
frag_id = self.add_shader(fragment, gl.GL_FRAGMENT_SHADER)
|
frag_id = self.add_shader(fragment, GL_FRAGMENT_SHADER)
|
||||||
gl.glAttachShader(self.program_id, frag_id)
|
glAttachShader(self.program_id, frag_id)
|
||||||
|
|
||||||
gl.glLinkProgram(self.program_id)
|
glLinkProgram(self.program_id)
|
||||||
if gl.glGetProgramiv(self.program_id, gl.GL_LINK_STATUS) != gl.GL_TRUE:
|
if glGetProgramiv(self.program_id, GL_LINK_STATUS) != GL_TRUE:
|
||||||
info = gl.glGetProgramInfoLog(self.program_id)
|
info = glGetProgramInfoLog(self.program_id)
|
||||||
gl.glDeleteProgram(self.program_id)
|
glDeleteProgram(self.program_id)
|
||||||
gl.glDeleteShader(vs_id)
|
glDeleteShader(vs_id)
|
||||||
gl.glDeleteShader(frag_id)
|
glDeleteShader(frag_id)
|
||||||
raise ValueError('Error linking shader program: \n%s' % info.decode('utf-8'))
|
raise ValueError('Error linking shader program: \n%s' % info.decode('utf-8'))
|
||||||
gl.glDeleteShader(vs_id)
|
glDeleteShader(vs_id)
|
||||||
gl.glDeleteShader(frag_id)
|
glDeleteShader(frag_id)
|
||||||
self.vao_id = gl.glGenVertexArrays(1)
|
self.vao_id = glGenVertexArrays(1)
|
||||||
|
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return self.program_id
|
return self.program_id
|
||||||
@@ -193,35 +195,35 @@ class ShaderProgram:
|
|||||||
|
|
||||||
def add_shader(self, source: str, shader_type: int) -> int:
|
def add_shader(self, source: str, shader_type: int) -> int:
|
||||||
' Compile a shader and return its id, or raise an exception if compilation fails '
|
' Compile a shader and return its id, or raise an exception if compilation fails '
|
||||||
shader_id = gl.glCreateShader(shader_type)
|
shader_id = glCreateShader(shader_type)
|
||||||
source = '#version {}\n{}'.format(VERSION, source)
|
source = '#version {}\n{}'.format(VERSION, source)
|
||||||
try:
|
try:
|
||||||
gl.glShaderSource(shader_id, source)
|
glShaderSource(shader_id, source)
|
||||||
gl.glCompileShader(shader_id)
|
glCompileShader(shader_id)
|
||||||
if gl.glGetShaderiv(shader_id, gl.GL_COMPILE_STATUS) != gl.GL_TRUE:
|
if glGetShaderiv(shader_id, GL_COMPILE_STATUS) != GL_TRUE:
|
||||||
info = gl.glGetShaderInfoLog(shader_id)
|
info = glGetShaderInfoLog(shader_id)
|
||||||
raise ValueError('GLSL {} compilation failed: \n{}'.format(shader_type, info.decode('utf-8')))
|
raise ValueError('GLSL {} compilation failed: \n{}'.format(shader_type, info.decode('utf-8')))
|
||||||
return shader_id
|
return shader_id
|
||||||
except Exception:
|
except Exception:
|
||||||
gl.glDeleteShader(shader_id)
|
glDeleteShader(shader_id)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@lru_cache(maxsize=None)
|
@lru_cache(maxsize=2**6)
|
||||||
def uniform_location(self, name: str) -> int:
|
def uniform_location(self, name: str) -> int:
|
||||||
' Return the id for the uniform variable `name` or -1 if not found. '
|
' Return the id for the uniform variable `name` or -1 if not found. '
|
||||||
return gl.glGetUniformLocation(self.program_id, name)
|
return glGetUniformLocation(self.program_id, name)
|
||||||
|
|
||||||
@lru_cache(maxsize=None)
|
@lru_cache(maxsize=2**6)
|
||||||
def attribute_location(self, name: str) -> int:
|
def attribute_location(self, name: str) -> int:
|
||||||
' Return the id for the attribute variable `name` or -1 if not found. '
|
' Return the id for the attribute variable `name` or -1 if not found. '
|
||||||
return gl.glGetAttribLocation(self.program_id, name)
|
return glGetAttribLocation(self.program_id, name)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
gl.glUseProgram(self.program_id)
|
glUseProgram(self.program_id)
|
||||||
gl.glBindVertexArray(self.vao_id)
|
glBindVertexArray(self.vao_id)
|
||||||
self.is_active = True
|
self.is_active = True
|
||||||
|
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
gl.glUseProgram(0)
|
glUseProgram(0)
|
||||||
gl.glBindVertexArray(0)
|
glBindVertexArray(0)
|
||||||
self.is_active = False
|
self.is_active = False
|
||||||
|
|||||||
Reference in New Issue
Block a user