OpenGL bindings for programs

This commit is contained in:
Kovid Goyal 2016-11-03 13:15:30 +05:30
parent 06b42a7e1c
commit 5d77499cb1
2 changed files with 227 additions and 33 deletions

View File

@ -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) \

View File

@ -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