OpenGL bindings for programs
This commit is contained in:
parent
06b42a7e1c
commit
5d77499cb1
194
kitty/gl.h
194
kitty/gl.h
@ -152,6 +152,175 @@ DrawArraysInstanced(PyObject UNUSED *self, PyObject *args) {
|
||||
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) {
|
||||
#define GLC(x) if (PyModule_AddIntConstant(module, #x, x) != 0) { PyErr_NoMemory(); return 0; }
|
||||
GLC(GL_VERSION);
|
||||
@ -160,21 +329,44 @@ int add_module_gl_constants(PyObject *module) {
|
||||
GLC(GL_RENDERER);
|
||||
GLC(GL_TRIANGLE_FAN);
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
#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(GetProgramiv, METH_VARARGS) \
|
||||
METH(GetShaderiv, METH_VARARGS) \
|
||||
METH(Uniform2ui, METH_VARARGS) \
|
||||
METH(Uniform1i, METH_VARARGS) \
|
||||
METH(Uniform2f, 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(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(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 .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)
|
||||
VERSION = GL_VERSION[0] * 100 + GL_VERSION[1] * 10
|
||||
|
||||
|
||||
def array(*args, dtype=gl.GLfloat):
|
||||
return (dtype * len(args))(*args)
|
||||
|
||||
|
||||
class Sprites:
|
||||
''' Maintain sprite sheets of all rendered characters on the GPU as a texture
|
||||
array with each texture being a sprite sheet. '''
|
||||
@ -163,24 +165,24 @@ class ShaderProgram:
|
||||
Create a shader program.
|
||||
|
||||
"""
|
||||
self.program_id = gl.glCreateProgram()
|
||||
self.program_id = glCreateProgram()
|
||||
self.is_active = False
|
||||
vs_id = self.add_shader(vertex, gl.GL_VERTEX_SHADER)
|
||||
gl.glAttachShader(self.program_id, vs_id)
|
||||
vs_id = self.add_shader(vertex, GL_VERTEX_SHADER)
|
||||
glAttachShader(self.program_id, vs_id)
|
||||
|
||||
frag_id = self.add_shader(fragment, gl.GL_FRAGMENT_SHADER)
|
||||
gl.glAttachShader(self.program_id, frag_id)
|
||||
frag_id = self.add_shader(fragment, GL_FRAGMENT_SHADER)
|
||||
glAttachShader(self.program_id, frag_id)
|
||||
|
||||
gl.glLinkProgram(self.program_id)
|
||||
if gl.glGetProgramiv(self.program_id, gl.GL_LINK_STATUS) != gl.GL_TRUE:
|
||||
info = gl.glGetProgramInfoLog(self.program_id)
|
||||
gl.glDeleteProgram(self.program_id)
|
||||
gl.glDeleteShader(vs_id)
|
||||
gl.glDeleteShader(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'))
|
||||
gl.glDeleteShader(vs_id)
|
||||
gl.glDeleteShader(frag_id)
|
||||
self.vao_id = gl.glGenVertexArrays(1)
|
||||
glDeleteShader(vs_id)
|
||||
glDeleteShader(frag_id)
|
||||
self.vao_id = glGenVertexArrays(1)
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return self.program_id
|
||||
@ -193,35 +195,35 @@ class ShaderProgram:
|
||||
|
||||
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 = gl.glCreateShader(shader_type)
|
||||
shader_id = glCreateShader(shader_type)
|
||||
source = '#version {}\n{}'.format(VERSION, source)
|
||||
try:
|
||||
gl.glShaderSource(shader_id, source)
|
||||
gl.glCompileShader(shader_id)
|
||||
if gl.glGetShaderiv(shader_id, gl.GL_COMPILE_STATUS) != gl.GL_TRUE:
|
||||
info = gl.glGetShaderInfoLog(shader_id)
|
||||
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:
|
||||
gl.glDeleteShader(shader_id)
|
||||
glDeleteShader(shader_id)
|
||||
raise
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
@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. '
|
||||
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:
|
||||
' 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):
|
||||
gl.glUseProgram(self.program_id)
|
||||
gl.glBindVertexArray(self.vao_id)
|
||||
glUseProgram(self.program_id)
|
||||
glBindVertexArray(self.vao_id)
|
||||
self.is_active = True
|
||||
|
||||
def __exit__(self, *args):
|
||||
gl.glUseProgram(0)
|
||||
gl.glBindVertexArray(0)
|
||||
glUseProgram(0)
|
||||
glBindVertexArray(0)
|
||||
self.is_active = False
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user