More work on migrating shader code

This commit is contained in:
Kovid Goyal
2017-09-11 08:12:13 +05:30
parent c1dc4d0575
commit 8ce2794a8a
4 changed files with 295 additions and 48 deletions

View File

@@ -29,7 +29,6 @@ extern int pthread_setname_np(const char *name);
#include <GLFW/glfw3.h>
#define EXTRA_FDS 2
#define MAX_CHILDREN 256
static void (*parse_func)(Screen*, PyObject*);

View File

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

View File

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

View File

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