#include "shader.h" static const char *vertex_shader = "\n\ #version 150 \n\ in vec4 aPosition;\n\ void main(void) {\n\ gl_Position = aPosition;\n\ }\n\ "; static GBytes *master_shader_f = NULL; static const gchar *master_shader_code; static gsize master_shader_code_size = 0; static GLuint create_shader(const char *source, GLenum type) { // Create the shader object GLuint shader = glCreateShader(type); // Load the shader source glShaderSource(shader, 1, &source, 0); // Compile the shader glCompileShader(shader); // Check for errors GLint status = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status == GL_FALSE) { GLchar messages[1024]; glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]); g_warning("GLSL Shader Error: %s", messages); } return shader; } static GLuint create_program(const char *vsh, const char *fsh) { // Build shaders GLuint vertex_shader = create_shader(vsh, GL_VERTEX_SHADER); GLuint fragment_shader = create_shader(fsh, GL_FRAGMENT_SHADER); // Create program GLuint program = glCreateProgram(); // Attach shaders glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); // Link program glLinkProgram(program); // Check for errors GLint status; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status == GL_FALSE) { GLchar messages[1024]; glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]); g_warning("GLSL Program Error: %s", messages); } // Delete shaders glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); return program; } bool init_shader_with_name(shader_t *shader, const char *name) { if (epoxy_gl_version() < 32) { return false; } GError *error = NULL; static char final_shader_code[0x10801] = {0,}; static signed long filter_token_location = 0; if (!master_shader_code_size) { master_shader_f = g_resources_lookup_data(RESOURCE_PREFIX "Shaders/MasterShader.fsh", G_RESOURCE_LOOKUP_FLAGS_NONE, &error); master_shader_code = g_bytes_get_data(master_shader_f, &master_shader_code_size); if (!master_shader_f) { g_warning("Failed to load master shader: %s", error->message); g_error_free(error); return false; } filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code; if (filter_token_location < 0) { g_error_free(error); return false; } } char shader_path[1024]; g_snprintf(shader_path, sizeof(shader_path), RESOURCE_PREFIX "Shaders/%s.fsh", name); GBytes *shader_f = g_resources_lookup_data(shader_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error); if (!shader_f) { g_warning("Failed to load shader \"%s\": %s", shader_path, error->message); g_error_free(error); return false; } gsize shader_code_size; const gchar *shader_code = g_bytes_get_data(shader_f, &shader_code_size); memset(final_shader_code, 0, sizeof(final_shader_code)); memcpy(final_shader_code, master_shader_code, filter_token_location); strcpy(final_shader_code + filter_token_location, shader_code); strcat(final_shader_code + filter_token_location, master_shader_code + filter_token_location + sizeof("{filter}") - 1); g_bytes_unref(shader_f); shader->program = create_program(vertex_shader, final_shader_code); // Attributes shader->position_attribute = glGetAttribLocation(shader->program, "aPosition"); // Uniforms shader->resolution_uniform = glGetUniformLocation(shader->program, "output_resolution"); shader->origin_uniform = glGetUniformLocation(shader->program, "origin"); glGenTextures(1, &shader->texture); glBindTexture(GL_TEXTURE_2D, shader->texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glBindTexture(GL_TEXTURE_2D, 0); shader->texture_uniform = glGetUniformLocation(shader->program, "image"); glGenTextures(1, &shader->previous_texture); glBindTexture(GL_TEXTURE_2D, shader->previous_texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glBindTexture(GL_TEXTURE_2D, 0); shader->previous_texture_uniform = glGetUniformLocation(shader->program, "previous_image"); shader->mix_previous_uniform = glGetUniformLocation(shader->program, "mix_previous"); // Program glUseProgram(shader->program); GLuint vao; glGenVertexArrays(1, &vao); glBindVertexArray(vao); GLuint vbo; glGenBuffers(1, &vbo); // Attributes static GLfloat const quad[16] = { -1.f, -1.f, 0, 1, -1.f, +1.f, 0, 1, +1.f, -1.f, 0, 1, +1.f, +1.f, 0, 1, }; glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW); glEnableVertexAttribArray(shader->position_attribute); glVertexAttribPointer(shader->position_attribute, 4, GL_FLOAT, GL_FALSE, 0, 0); return true; } void render_bitmap_with_shader(shader_t *shader, void *bitmap, void *previous, unsigned source_width, unsigned source_height, unsigned x, unsigned y, unsigned w, unsigned h) { glUseProgram(shader->program); glUniform2f(shader->origin_uniform, x, y); glUniform2f(shader->resolution_uniform, w, h); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, shader->texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_width, source_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, bitmap); glUniform1i(shader->texture_uniform, 0); glUniform1i(shader->mix_previous_uniform, previous != NULL); if (previous) { glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, shader->previous_texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, source_width, source_height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, previous); glUniform1i(shader->previous_texture_uniform, 1); } glBindFragDataLocation(shader->program, 0, "frag_color"); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } void free_shader(shader_t *shader) { if (epoxy_gl_version() < 32) { return; } glDeleteProgram(shader->program); glDeleteTextures(1, &shader->texture); glDeleteTextures(1, &shader->previous_texture); } void free_master_shader(void) { g_bytes_unref(master_shader_f); master_shader_code_size = 0; }