mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-09 15:08:13 +02:00
More work on migrating shader code
This commit is contained in:
@@ -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*);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
26
kitty/gl.c
26
kitty/gl.c
@@ -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;
|
||||
}
|
||||
|
||||
315
kitty/shaders.c
315
kitty/shaders.c
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user