mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-09-21 19:47:21 +03:00
Move the kitty OpenGL API into its own compilation unit
This commit is contained in:
parent
64ffca2d0a
commit
40b9049deb
366
kitty/gl.c
Normal file
366
kitty/gl.c
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
/*
|
||||||
|
* gl.c
|
||||||
|
* Copyright (C) 2019 Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
*
|
||||||
|
* Distributed under terms of the GPL3 license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "gl.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include "glfw-wrapper.h"
|
||||||
|
#include "state.h"
|
||||||
|
|
||||||
|
// GL setup and error handling {{{
|
||||||
|
static void
|
||||||
|
check_for_gl_error(void UNUSED *ret, const char *name, GLADapiproc UNUSED funcptr, int UNUSED len_args, ...) {
|
||||||
|
#define f(msg) fatal("OpenGL error: %s (calling function: %s)", msg, name); break;
|
||||||
|
GLenum code = glad_glGetError();
|
||||||
|
switch(code) {
|
||||||
|
case GL_NO_ERROR: break;
|
||||||
|
case GL_INVALID_ENUM:
|
||||||
|
f("An enum value is invalid (GL_INVALID_ENUM)");
|
||||||
|
case GL_INVALID_VALUE:
|
||||||
|
f("An numeric value is invalid (GL_INVALID_VALUE)");
|
||||||
|
case GL_INVALID_OPERATION:
|
||||||
|
f("This operation is invalid (GL_INVALID_OPERATION)");
|
||||||
|
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||||
|
f("The framebuffer object is not complete (GL_INVALID_FRAMEBUFFER_OPERATION)");
|
||||||
|
case GL_OUT_OF_MEMORY:
|
||||||
|
f("There is not enough memory left to execute the command. (GL_OUT_OF_MEMORY)");
|
||||||
|
case GL_STACK_UNDERFLOW:
|
||||||
|
f("An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_UNDERFLOW)");
|
||||||
|
case GL_STACK_OVERFLOW:
|
||||||
|
f("An attempt has been made to perform an operation that would cause an internal stack to overflow. (GL_STACK_OVERFLOW)");
|
||||||
|
default:
|
||||||
|
fatal("An unknown OpenGL error occurred with code: %d (calling function: %s)", code, name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gl_init() {
|
||||||
|
static bool glad_loaded = false;
|
||||||
|
if (!glad_loaded) {
|
||||||
|
int gl_version = gladLoadGL(glfwGetProcAddress);
|
||||||
|
if (!gl_version) {
|
||||||
|
fatal("Loading the OpenGL library failed");
|
||||||
|
}
|
||||||
|
if (!global_state.debug_gl) {
|
||||||
|
gladUninstallGLDebug();
|
||||||
|
}
|
||||||
|
gladSetGLPostCallback(check_for_gl_error);
|
||||||
|
#define ARB_TEST(name) \
|
||||||
|
if (!GLAD_GL_ARB_##name) { \
|
||||||
|
fatal("The OpenGL driver on this system is missing the required extension: ARB_%s", #name); \
|
||||||
|
}
|
||||||
|
ARB_TEST(texture_storage);
|
||||||
|
#undef ARB_TEST
|
||||||
|
glad_loaded = true;
|
||||||
|
int gl_major = GLAD_VERSION_MAJOR(gl_version);
|
||||||
|
int gl_minor = GLAD_VERSION_MINOR(gl_version);
|
||||||
|
if (global_state.debug_gl) printf("GL version string: '%s' Detected version: %d.%d\n", glGetString(GL_VERSION), gl_major, gl_minor);
|
||||||
|
if (gl_major < OPENGL_REQUIRED_VERSION_MAJOR || (gl_major == OPENGL_REQUIRED_VERSION_MAJOR && gl_minor < OPENGL_REQUIRED_VERSION_MINOR)) {
|
||||||
|
fatal("OpenGL version is %d.%d, version >= 3.3 required for kitty", gl_major, gl_minor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
update_surface_size(int w, int h, GLuint offscreen_texture_id) {
|
||||||
|
glViewport(0, 0, w, h);
|
||||||
|
if (offscreen_texture_id) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, offscreen_texture_id);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
free_texture(GLuint *tex_id) {
|
||||||
|
glDeleteTextures(1, tex_id);
|
||||||
|
*tex_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
// Programs {{{
|
||||||
|
|
||||||
|
static Program programs[64] = {{0}};
|
||||||
|
|
||||||
|
GLuint
|
||||||
|
compile_shader(GLenum shader_type, const char *source) {
|
||||||
|
GLuint shader_id = glCreateShader(shader_type);
|
||||||
|
glShaderSource(shader_id, 1, (const GLchar **)&source, NULL);
|
||||||
|
glCompileShader(shader_id);
|
||||||
|
GLint ret = GL_FALSE;
|
||||||
|
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &ret);
|
||||||
|
if (ret != GL_TRUE) {
|
||||||
|
GLsizei len;
|
||||||
|
static char glbuf[4096];
|
||||||
|
glGetShaderInfoLog(shader_id, sizeof(glbuf), &len, glbuf);
|
||||||
|
log_error("Failed to compile GLSL shader!\n%s", glbuf);
|
||||||
|
glDeleteShader(shader_id);
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return shader_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program*
|
||||||
|
program_ptr(int program) { return programs + (size_t)program; }
|
||||||
|
|
||||||
|
GLuint
|
||||||
|
program_id(int program) { return programs[program].id; }
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
init_uniforms(int program) {
|
||||||
|
Program *p = programs + program;
|
||||||
|
glGetProgramiv(p->id, GL_ACTIVE_UNIFORMS, &(p->num_of_uniforms));
|
||||||
|
for (GLint i = 0; i < p->num_of_uniforms; i++) {
|
||||||
|
Uniform *u = p->uniforms + i;
|
||||||
|
glGetActiveUniform(p->id, (GLuint)i, sizeof(u->name)/sizeof(u->name[0]), NULL, &(u->size), &(u->type), u->name);
|
||||||
|
u->location = glGetUniformLocation(p->id, u->name);
|
||||||
|
u->idx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint
|
||||||
|
get_uniform_information(int program, const char *name, GLenum information_type) {
|
||||||
|
GLint q; GLuint t;
|
||||||
|
static const char* names[] = {""};
|
||||||
|
names[0] = name;
|
||||||
|
GLuint pid = program_id(program);
|
||||||
|
glGetUniformIndices(pid, 1, (void*)names, &t);
|
||||||
|
glGetActiveUniformsiv(pid, 1, &t, information_type, &q);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint
|
||||||
|
attrib_location(int program, const char *name) {
|
||||||
|
GLint ans = glGetAttribLocation(programs[program].id, name);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint
|
||||||
|
block_index(int program, const char *name) {
|
||||||
|
GLuint ans = glGetUniformBlockIndex(programs[program].id, name);
|
||||||
|
if (ans == GL_INVALID_INDEX) { fatal("Could not find block index"); }
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GLint
|
||||||
|
block_size(int program, GLuint block_index) {
|
||||||
|
GLint ans;
|
||||||
|
glGetActiveUniformBlockiv(programs[program].id, block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ans);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bind_program(int program) {
|
||||||
|
glUseProgram(programs[program].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unbind_program(void) {
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
// Buffers {{{
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GLuint id;
|
||||||
|
GLsizeiptr size;
|
||||||
|
GLenum usage;
|
||||||
|
} Buffer;
|
||||||
|
|
||||||
|
|
||||||
|
static Buffer buffers[MAX_CHILDREN * 6 + 4] = {{0}};
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
create_buffer(GLenum usage) {
|
||||||
|
GLuint buffer_id;
|
||||||
|
glGenBuffers(1, &buffer_id);
|
||||||
|
for (size_t i = 0; i < sizeof(buffers)/sizeof(buffers[0]); i++) {
|
||||||
|
if (buffers[i].id == 0) {
|
||||||
|
buffers[i].id = buffer_id;
|
||||||
|
buffers[i].size = 0;
|
||||||
|
buffers[i].usage = usage;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glDeleteBuffers(1, &buffer_id);
|
||||||
|
fatal("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 GLuint
|
||||||
|
bind_buffer(ssize_t buf_idx) {
|
||||||
|
glBindBuffer(buffers[buf_idx].usage, buffers[buf_idx].id);
|
||||||
|
return buffers[buf_idx].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
unbind_buffer(ssize_t buf_idx) {
|
||||||
|
glBindBuffer(buffers[buf_idx].usage, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
alloc_buffer(ssize_t idx, GLsizeiptr size, GLenum usage) {
|
||||||
|
Buffer *b = buffers + idx;
|
||||||
|
if (b->size == size) return;
|
||||||
|
b->size = size;
|
||||||
|
glBufferData(b->usage, size, NULL, usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void*
|
||||||
|
map_buffer(ssize_t idx, GLenum access) {
|
||||||
|
void *ans = glMapBuffer(buffers[idx].usage, access);
|
||||||
|
return ans;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
unmap_buffer(ssize_t idx) {
|
||||||
|
glUnmapBuffer(buffers[idx].usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
// Vertex Array Objects (VAO) {{{
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
GLuint id;
|
||||||
|
size_t num_buffers;
|
||||||
|
ssize_t buffers[10];
|
||||||
|
} VAO;
|
||||||
|
|
||||||
|
static VAO vaos[4*MAX_CHILDREN + 10] = {{0}};
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
create_vao(void) {
|
||||||
|
GLuint vao_id;
|
||||||
|
glGenVertexArrays(1, &vao_id);
|
||||||
|
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);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glDeleteVertexArrays(1, &vao_id);
|
||||||
|
fatal("Too many VAOs");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
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])) {
|
||||||
|
fatal("Too many buffers in a single VAO");
|
||||||
|
}
|
||||||
|
ssize_t buf = create_buffer(usage);
|
||||||
|
vao->buffers[vao->num_buffers++] = buf;
|
||||||
|
return vao->num_buffers - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_located_attribute_to_vao(ssize_t vao_idx, GLint aloc, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {
|
||||||
|
VAO *vao = vaos + vao_idx;
|
||||||
|
if (!vao->num_buffers) fatal("You must create a buffer for this attribute first");
|
||||||
|
ssize_t buf = vao->buffers[vao->num_buffers - 1];
|
||||||
|
bind_buffer(buf);
|
||||||
|
glEnableVertexAttribArray(aloc);
|
||||||
|
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(aloc, size, data_type, stride, offset);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
glVertexAttribPointer(aloc, size, data_type, GL_FALSE, stride, offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (divisor) {
|
||||||
|
glVertexAttribDivisor(aloc, divisor);
|
||||||
|
}
|
||||||
|
unbind_buffer(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
add_attribute_to_vao(int p, ssize_t vao_idx, const char *name, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {
|
||||||
|
GLint aloc = attrib_location(p, name);
|
||||||
|
if (aloc == -1) fatal("No attribute named: %s found in this program", name);
|
||||||
|
add_located_attribute_to_vao(vao_idx, aloc, size, data_type, stride, offset, divisor);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bind_vertex_array(ssize_t vao_idx) {
|
||||||
|
glBindVertexArray(vaos[vao_idx].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unbind_vertex_array(void) {
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t
|
||||||
|
alloc_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, GLenum usage) {
|
||||||
|
ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
|
||||||
|
bind_buffer(buf_idx);
|
||||||
|
alloc_buffer(buf_idx, size, usage);
|
||||||
|
return buf_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
map_vao_buffer(ssize_t vao_idx, size_t bufnum, GLenum access) {
|
||||||
|
ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
|
||||||
|
bind_buffer(buf_idx);
|
||||||
|
return map_buffer(buf_idx, access);
|
||||||
|
}
|
||||||
|
|
||||||
|
void*
|
||||||
|
alloc_and_map_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, GLenum usage, GLenum access) {
|
||||||
|
ssize_t buf_idx = alloc_vao_buffer(vao_idx, size, bufnum, usage);
|
||||||
|
return map_buffer(buf_idx, access);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bind_vao_uniform_buffer(ssize_t vao_idx, size_t bufnum, GLuint block_index) {
|
||||||
|
ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
|
||||||
|
glBindBufferBase(GL_UNIFORM_BUFFER, block_index, buffers[buf_idx].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unmap_vao_buffer(ssize_t vao_idx, size_t bufnum) {
|
||||||
|
ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
|
||||||
|
unmap_buffer(buf_idx);
|
||||||
|
unbind_buffer(buf_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// }}}
|
379
kitty/gl.h
379
kitty/gl.h
@ -6,92 +6,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "data-types.h"
|
||||||
#include "gl-wrapper.h"
|
#include "gl-wrapper.h"
|
||||||
#include "state.h"
|
|
||||||
#include "screen.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include "glfw-wrapper.h"
|
|
||||||
|
|
||||||
static char glbuf[4096];
|
|
||||||
|
|
||||||
// GL setup and error handling {{{
|
|
||||||
#define GLSL_VERSION (OPENGL_REQUIRED_VERSION_MAJOR * 100 + OPENGL_REQUIRED_VERSION_MINOR * 10)
|
#define GLSL_VERSION (OPENGL_REQUIRED_VERSION_MAJOR * 100 + OPENGL_REQUIRED_VERSION_MINOR * 10)
|
||||||
|
|
||||||
static void
|
|
||||||
check_for_gl_error(void UNUSED *ret, const char *name, GLADapiproc UNUSED funcptr, int UNUSED len_args, ...) {
|
|
||||||
#define f(msg) fatal("OpenGL error: %s (calling function: %s)", msg, name); break;
|
|
||||||
GLenum code = glad_glGetError();
|
|
||||||
switch(code) {
|
|
||||||
case GL_NO_ERROR: break;
|
|
||||||
case GL_INVALID_ENUM:
|
|
||||||
f("An enum value is invalid (GL_INVALID_ENUM)");
|
|
||||||
case GL_INVALID_VALUE:
|
|
||||||
f("An numeric value is invalid (GL_INVALID_VALUE)");
|
|
||||||
case GL_INVALID_OPERATION:
|
|
||||||
f("This operation is invalid (GL_INVALID_OPERATION)");
|
|
||||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
|
||||||
f("The framebuffer object is not complete (GL_INVALID_FRAMEBUFFER_OPERATION)");
|
|
||||||
case GL_OUT_OF_MEMORY:
|
|
||||||
f("There is not enough memory left to execute the command. (GL_OUT_OF_MEMORY)");
|
|
||||||
case GL_STACK_UNDERFLOW:
|
|
||||||
f("An attempt has been made to perform an operation that would cause an internal stack to underflow. (GL_STACK_UNDERFLOW)");
|
|
||||||
case GL_STACK_OVERFLOW:
|
|
||||||
f("An attempt has been made to perform an operation that would cause an internal stack to overflow. (GL_STACK_OVERFLOW)");
|
|
||||||
default:
|
|
||||||
fatal("An unknown OpenGL error occurred with code: %d (calling function: %s)", code, name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
gl_init() {
|
|
||||||
static bool glad_loaded = false;
|
|
||||||
if (!glad_loaded) {
|
|
||||||
int gl_version = gladLoadGL(glfwGetProcAddress);
|
|
||||||
if (!gl_version) {
|
|
||||||
fatal("Loading the OpenGL library failed");
|
|
||||||
}
|
|
||||||
if (!global_state.debug_gl) {
|
|
||||||
gladUninstallGLDebug();
|
|
||||||
}
|
|
||||||
gladSetGLPostCallback(check_for_gl_error);
|
|
||||||
#define ARB_TEST(name) \
|
|
||||||
if (!GLAD_GL_ARB_##name) { \
|
|
||||||
fatal("The OpenGL driver on this system is missing the required extension: ARB_%s", #name); \
|
|
||||||
}
|
|
||||||
ARB_TEST(texture_storage);
|
|
||||||
#undef ARB_TEST
|
|
||||||
glad_loaded = true;
|
|
||||||
int gl_major = GLAD_VERSION_MAJOR(gl_version);
|
|
||||||
int gl_minor = GLAD_VERSION_MINOR(gl_version);
|
|
||||||
if (global_state.debug_gl) printf("GL version string: '%s' Detected version: %d.%d\n", glGetString(GL_VERSION), gl_major, gl_minor);
|
|
||||||
if (gl_major < OPENGL_REQUIRED_VERSION_MAJOR || (gl_major == OPENGL_REQUIRED_VERSION_MAJOR && gl_minor < OPENGL_REQUIRED_VERSION_MINOR)) {
|
|
||||||
fatal("OpenGL version is %d.%d, version >= 3.3 required for kitty", gl_major, gl_minor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
update_surface_size(int w, int h, GLuint offscreen_texture_id) {
|
|
||||||
glViewport(0, 0, w, h);
|
|
||||||
if (offscreen_texture_id) {
|
|
||||||
glBindTexture(GL_TEXTURE_2D, offscreen_texture_id);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
free_texture(GLuint *tex_id) {
|
|
||||||
glDeleteTextures(1, tex_id);
|
|
||||||
*tex_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
// Programs {{{
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
GLint size, index;
|
GLint size, index;
|
||||||
} UniformBlock;
|
} UniformBlock;
|
||||||
@ -112,276 +31,28 @@ typedef struct {
|
|||||||
GLint num_of_uniforms;
|
GLint num_of_uniforms;
|
||||||
} Program;
|
} Program;
|
||||||
|
|
||||||
static Program programs[64] = {{0}};
|
|
||||||
|
|
||||||
static inline GLuint
|
void gl_init(void);
|
||||||
compile_shader(GLenum shader_type, const char *source) {
|
void update_surface_size(int w, int h, GLuint offscreen_texture_id);
|
||||||
GLuint shader_id = glCreateShader(shader_type);
|
void free_texture(GLuint *tex_id);
|
||||||
glShaderSource(shader_id, 1, (const GLchar **)&source, NULL);
|
void remove_vao(ssize_t vao_idx);
|
||||||
glCompileShader(shader_id);
|
void init_uniforms(int program);
|
||||||
GLint ret = GL_FALSE;
|
GLuint program_id(int program);
|
||||||
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &ret);
|
Program* program_ptr(int program);
|
||||||
if (ret != GL_TRUE) {
|
GLuint block_index(int program, const char *name);
|
||||||
GLsizei len;
|
GLint block_size(int program, GLuint block_index);
|
||||||
glGetShaderInfoLog(shader_id, sizeof(glbuf), &len, glbuf);
|
GLint get_uniform_information(int program, const char *name, GLenum information_type);
|
||||||
log_error("Failed to compile GLSL shader!\n%s", glbuf);
|
GLint attrib_location(int program, const char *name);
|
||||||
glDeleteShader(shader_id);
|
ssize_t create_vao(void);
|
||||||
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
size_t add_buffer_to_vao(ssize_t vao_idx, GLenum usage);
|
||||||
return 0;
|
void add_attribute_to_vao(int p, ssize_t vao_idx, const char *name, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor);
|
||||||
}
|
ssize_t alloc_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, GLenum usage);
|
||||||
return shader_id;
|
void* alloc_and_map_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, GLenum usage, GLenum access);
|
||||||
}
|
void unmap_vao_buffer(ssize_t vao_idx, size_t bufnum);
|
||||||
|
void* map_vao_buffer(ssize_t vao_idx, size_t bufnum, GLenum access);
|
||||||
static inline GLuint
|
void bind_program(int program);
|
||||||
program_id(int program) { return programs[program].id; }
|
void bind_vertex_array(ssize_t vao_idx);
|
||||||
|
void bind_vao_uniform_buffer(ssize_t vao_idx, size_t bufnum, GLuint block_index);
|
||||||
static inline void
|
void unbind_vertex_array(void);
|
||||||
init_uniforms(int program) {
|
void unbind_program(void);
|
||||||
Program *p = programs + program;
|
GLuint compile_shader(GLenum shader_type, const char *source);
|
||||||
glGetProgramiv(p->id, GL_ACTIVE_UNIFORMS, &(p->num_of_uniforms));
|
|
||||||
for (GLint i = 0; i < p->num_of_uniforms; i++) {
|
|
||||||
Uniform *u = p->uniforms + i;
|
|
||||||
glGetActiveUniform(p->id, (GLuint)i, sizeof(u->name)/sizeof(u->name[0]), NULL, &(u->size), &(u->type), u->name);
|
|
||||||
u->location = glGetUniformLocation(p->id, u->name);
|
|
||||||
u->idx = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline GLint
|
|
||||||
get_uniform_information(int program, const char *name, GLenum information_type) {
|
|
||||||
GLint q; GLuint t;
|
|
||||||
static const char* names[] = {""};
|
|
||||||
names[0] = name;
|
|
||||||
GLuint pid = program_id(program);
|
|
||||||
glGetUniformIndices(pid, 1, (void*)names, &t);
|
|
||||||
glGetActiveUniformsiv(pid, 1, &t, information_type, &q);
|
|
||||||
return q;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline GLint
|
|
||||||
attrib_location(int program, const char *name) {
|
|
||||||
GLint ans = glGetAttribLocation(programs[program].id, name);
|
|
||||||
return ans;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline GLuint
|
|
||||||
block_index(int program, const char *name) {
|
|
||||||
GLuint ans = glGetUniformBlockIndex(programs[program].id, name);
|
|
||||||
if (ans == GL_INVALID_INDEX) { fatal("Could not find block index"); }
|
|
||||||
return ans;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline GLint
|
|
||||||
block_size(int program, GLuint block_index) {
|
|
||||||
GLint ans;
|
|
||||||
glGetActiveUniformBlockiv(programs[program].id, block_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ans);
|
|
||||||
return ans;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
bind_program(int program) {
|
|
||||||
glUseProgram(programs[program].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
unbind_program(void) {
|
|
||||||
glUseProgram(0);
|
|
||||||
}
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
// Buffers {{{
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
GLuint id;
|
|
||||||
GLsizeiptr size;
|
|
||||||
GLenum usage;
|
|
||||||
} Buffer;
|
|
||||||
|
|
||||||
|
|
||||||
static Buffer buffers[MAX_CHILDREN * 6 + 4] = {{0}};
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
create_buffer(GLenum usage) {
|
|
||||||
GLuint buffer_id;
|
|
||||||
glGenBuffers(1, &buffer_id);
|
|
||||||
for (size_t i = 0; i < sizeof(buffers)/sizeof(buffers[0]); i++) {
|
|
||||||
if (buffers[i].id == 0) {
|
|
||||||
buffers[i].id = buffer_id;
|
|
||||||
buffers[i].size = 0;
|
|
||||||
buffers[i].usage = usage;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glDeleteBuffers(1, &buffer_id);
|
|
||||||
fatal("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 GLuint
|
|
||||||
bind_buffer(ssize_t buf_idx) {
|
|
||||||
glBindBuffer(buffers[buf_idx].usage, buffers[buf_idx].id);
|
|
||||||
return buffers[buf_idx].id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
unbind_buffer(ssize_t buf_idx) {
|
|
||||||
glBindBuffer(buffers[buf_idx].usage, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
alloc_buffer(ssize_t idx, GLsizeiptr size, GLenum usage) {
|
|
||||||
Buffer *b = buffers + idx;
|
|
||||||
if (b->size == size) return;
|
|
||||||
b->size = size;
|
|
||||||
glBufferData(b->usage, size, NULL, usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void*
|
|
||||||
map_buffer(ssize_t idx, GLenum access) {
|
|
||||||
void *ans = glMapBuffer(buffers[idx].usage, access);
|
|
||||||
return ans;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
unmap_buffer(ssize_t idx) {
|
|
||||||
glUnmapBuffer(buffers[idx].usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// }}}
|
|
||||||
|
|
||||||
// Vertex Array Objects (VAO) {{{
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
GLuint id;
|
|
||||||
size_t num_buffers;
|
|
||||||
ssize_t buffers[10];
|
|
||||||
} VAO;
|
|
||||||
|
|
||||||
static VAO vaos[4*MAX_CHILDREN + 10] = {{0}};
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
create_vao(void) {
|
|
||||||
GLuint vao_id;
|
|
||||||
glGenVertexArrays(1, &vao_id);
|
|
||||||
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);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glDeleteVertexArrays(1, &vao_id);
|
|
||||||
fatal("Too many VAOs");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t
|
|
||||||
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])) {
|
|
||||||
fatal("Too many buffers in a single VAO");
|
|
||||||
}
|
|
||||||
ssize_t buf = create_buffer(usage);
|
|
||||||
vao->buffers[vao->num_buffers++] = buf;
|
|
||||||
return vao->num_buffers - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_located_attribute_to_vao(ssize_t vao_idx, GLint aloc, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {
|
|
||||||
VAO *vao = vaos + vao_idx;
|
|
||||||
if (!vao->num_buffers) fatal("You must create a buffer for this attribute first");
|
|
||||||
ssize_t buf = vao->buffers[vao->num_buffers - 1];
|
|
||||||
bind_buffer(buf);
|
|
||||||
glEnableVertexAttribArray(aloc);
|
|
||||||
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(aloc, size, data_type, stride, offset);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
glVertexAttribPointer(aloc, size, data_type, GL_FALSE, stride, offset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (divisor) {
|
|
||||||
glVertexAttribDivisor(aloc, divisor);
|
|
||||||
}
|
|
||||||
unbind_buffer(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
add_attribute_to_vao(int p, ssize_t vao_idx, const char *name, GLint size, GLenum data_type, GLsizei stride, void *offset, GLuint divisor) {
|
|
||||||
GLint aloc = attrib_location(p, name);
|
|
||||||
if (aloc == -1) fatal("No attribute named: %s found in this program", name);
|
|
||||||
add_located_attribute_to_vao(vao_idx, aloc, size, data_type, stride, offset, divisor);
|
|
||||||
}
|
|
||||||
|
|
||||||
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(void) {
|
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
alloc_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, GLenum usage) {
|
|
||||||
ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
|
|
||||||
bind_buffer(buf_idx);
|
|
||||||
alloc_buffer(buf_idx, size, usage);
|
|
||||||
return buf_idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void*
|
|
||||||
map_vao_buffer(ssize_t vao_idx, size_t bufnum, GLenum access) {
|
|
||||||
ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
|
|
||||||
bind_buffer(buf_idx);
|
|
||||||
return map_buffer(buf_idx, access);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void*
|
|
||||||
alloc_and_map_vao_buffer(ssize_t vao_idx, GLsizeiptr size, size_t bufnum, GLenum usage, GLenum access) {
|
|
||||||
ssize_t buf_idx = alloc_vao_buffer(vao_idx, size, bufnum, usage);
|
|
||||||
return map_buffer(buf_idx, access);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
bind_vao_uniform_buffer(ssize_t vao_idx, size_t bufnum, GLuint block_index) {
|
|
||||||
ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
|
|
||||||
glBindBufferBase(GL_UNIFORM_BUFFER, block_index, buffers[buf_idx].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
unmap_vao_buffer(ssize_t vao_idx, size_t bufnum) {
|
|
||||||
ssize_t buf_idx = vaos[vao_idx].buffers[bufnum];
|
|
||||||
unmap_buffer(buf_idx);
|
|
||||||
unbind_buffer(buf_idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
// }}}
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "fonts.h"
|
#include "fonts.h"
|
||||||
#include "gl.h"
|
#include "gl.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BLIT_PROGRAM, NUM_PROGRAMS };
|
enum { CELL_PROGRAM, CELL_BG_PROGRAM, CELL_SPECIAL_PROGRAM, CELL_FG_PROGRAM, BORDERS_PROGRAM, GRAPHICS_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_ALPHA_MASK_PROGRAM, BLIT_PROGRAM, NUM_PROGRAMS };
|
||||||
enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BLIT_UNIT };
|
enum { SPRITE_MAP_UNIT, GRAPHICS_UNIT, BLIT_UNIT };
|
||||||
@ -533,7 +534,7 @@ static GLint border_uniform_locations[NUM_BORDER_UNIFORMS] = {0};
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
init_borders_program(void) {
|
init_borders_program(void) {
|
||||||
Program *p = programs + BORDERS_PROGRAM;
|
Program *p = program_ptr(BORDERS_PROGRAM);
|
||||||
int left = NUM_BORDER_UNIFORMS;
|
int left = NUM_BORDER_UNIFORMS;
|
||||||
for (int i = 0; i < p->num_of_uniforms; i++, left--) {
|
for (int i = 0; i < p->num_of_uniforms; i++, left--) {
|
||||||
#define SET_LOC(which) (strcmp(p->uniforms[i].name, #which) == 0) border_uniform_locations[BORDER_##which] = p->uniforms[i].location
|
#define SET_LOC(which) (strcmp(p->uniforms[i].name, #which) == 0) border_uniform_locations[BORDER_##which] = p->uniforms[i].location
|
||||||
@ -598,18 +599,20 @@ compile_program(PyObject UNUSED *self, PyObject *args) {
|
|||||||
GLuint vertex_shader_id = 0, fragment_shader_id = 0;
|
GLuint vertex_shader_id = 0, fragment_shader_id = 0;
|
||||||
if (!PyArg_ParseTuple(args, "iss", &which, &vertex_shader, &fragment_shader)) return NULL;
|
if (!PyArg_ParseTuple(args, "iss", &which, &vertex_shader, &fragment_shader)) return NULL;
|
||||||
if (which < 0 || which >= NUM_PROGRAMS) { PyErr_Format(PyExc_ValueError, "Unknown program: %d", which); return NULL; }
|
if (which < 0 || which >= NUM_PROGRAMS) { PyErr_Format(PyExc_ValueError, "Unknown program: %d", which); return NULL; }
|
||||||
if (programs[which].id != 0) { PyErr_SetString(PyExc_ValueError, "program already compiled"); return NULL; }
|
Program *program = program_ptr(which);
|
||||||
programs[which].id = glCreateProgram();
|
if (program->id != 0) { PyErr_SetString(PyExc_ValueError, "program already compiled"); return NULL; }
|
||||||
|
program->id = glCreateProgram();
|
||||||
vertex_shader_id = compile_shader(GL_VERTEX_SHADER, vertex_shader);
|
vertex_shader_id = compile_shader(GL_VERTEX_SHADER, vertex_shader);
|
||||||
fragment_shader_id = compile_shader(GL_FRAGMENT_SHADER, fragment_shader);
|
fragment_shader_id = compile_shader(GL_FRAGMENT_SHADER, fragment_shader);
|
||||||
glAttachShader(programs[which].id, vertex_shader_id);
|
glAttachShader(program->id, vertex_shader_id);
|
||||||
glAttachShader(programs[which].id, fragment_shader_id);
|
glAttachShader(program->id, fragment_shader_id);
|
||||||
glLinkProgram(programs[which].id);
|
glLinkProgram(program->id);
|
||||||
GLint ret = GL_FALSE;
|
GLint ret = GL_FALSE;
|
||||||
glGetProgramiv(programs[which].id, GL_LINK_STATUS, &ret);
|
glGetProgramiv(program->id, GL_LINK_STATUS, &ret);
|
||||||
if (ret != GL_TRUE) {
|
if (ret != GL_TRUE) {
|
||||||
GLsizei len;
|
GLsizei len;
|
||||||
glGetProgramInfoLog(programs[which].id, sizeof(glbuf), &len, glbuf);
|
static char glbuf[4096];
|
||||||
|
glGetProgramInfoLog(program->id, sizeof(glbuf), &len, glbuf);
|
||||||
log_error("Failed to compile GLSL shader!\n%s", glbuf);
|
log_error("Failed to compile GLSL shader!\n%s", glbuf);
|
||||||
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
PyErr_SetString(PyExc_ValueError, "Failed to compile shader");
|
||||||
goto end;
|
goto end;
|
||||||
@ -619,8 +622,8 @@ compile_program(PyObject UNUSED *self, PyObject *args) {
|
|||||||
end:
|
end:
|
||||||
if (vertex_shader_id != 0) glDeleteShader(vertex_shader_id);
|
if (vertex_shader_id != 0) glDeleteShader(vertex_shader_id);
|
||||||
if (fragment_shader_id != 0) glDeleteShader(fragment_shader_id);
|
if (fragment_shader_id != 0) glDeleteShader(fragment_shader_id);
|
||||||
if (PyErr_Occurred()) { glDeleteProgram(programs[which].id); programs[which].id = 0; return NULL;}
|
if (PyErr_Occurred()) { glDeleteProgram(program->id); program->id = 0; return NULL;}
|
||||||
return Py_BuildValue("I", programs[which].id);
|
return Py_BuildValue("I", program->id);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user