207 lines
7.0 KiB
C
207 lines
7.0 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "shader.h"
|
|
#include "utils.h"
|
|
|
|
static const char *vertex_shader = "\n\
|
|
#version 150 \n\
|
|
in vec4 aPosition;\n\
|
|
void main(void) {\n\
|
|
gl_Position = aPosition;\n\
|
|
}\n\
|
|
";
|
|
|
|
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]);
|
|
fprintf(stderr, "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]);
|
|
fprintf(stderr, "GLSL Program Error: %s", messages);
|
|
}
|
|
|
|
// Delete shaders
|
|
glDeleteShader(vertex_shader);
|
|
glDeleteShader(fragment_shader);
|
|
|
|
return program;
|
|
}
|
|
|
|
extern bool uses_gl(void);
|
|
bool init_shader_with_name(shader_t *shader, const char *name)
|
|
{
|
|
if (!uses_gl()) return false;
|
|
|
|
GLint major = 0, minor = 0;
|
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
|
|
|
if (major * 0x100 + minor < 0x302) {
|
|
return false;
|
|
}
|
|
|
|
static char master_shader_code[0x801] = {0,};
|
|
static char shader_code[0x10001] = {0,};
|
|
static char final_shader_code[0x10801] = {0,};
|
|
static ssize_t filter_token_location = 0;
|
|
|
|
if (!master_shader_code[0]) {
|
|
FILE *master_shader_f = fopen(resource_path("Shaders/MasterShader.fsh"), "r");
|
|
if (!master_shader_f) return false;
|
|
fread(master_shader_code, 1, sizeof(master_shader_code) - 1, master_shader_f);
|
|
fclose(master_shader_f);
|
|
filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code;
|
|
if (filter_token_location < 0) {
|
|
master_shader_code[0] = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
char shader_path[1024];
|
|
sprintf(shader_path, "Shaders/%s.fsh", name);
|
|
|
|
FILE *shader_f = fopen(resource_path(shader_path), "r");
|
|
if (!shader_f) return false;
|
|
memset(shader_code, 0, sizeof(shader_code));
|
|
fread(shader_code, 1, sizeof(shader_code) - 1, shader_f);
|
|
fclose(shader_f);
|
|
|
|
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);
|
|
|
|
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_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
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_EDGE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
shader->previous_texture_uniform = glGetUniformLocation(shader->program, "previous_image");
|
|
|
|
shader->blending_mode_uniform = glGetUniformLocation(shader->program, "frame_blending_mode");
|
|
|
|
// 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,
|
|
GB_frame_blending_mode_t blending_mode)
|
|
{
|
|
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_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
|
|
glUniform1i(shader->texture_uniform, 0);
|
|
glUniform1i(shader->blending_mode_uniform, previous? blending_mode : GB_FRAME_BLENDING_MODE_DISABLED);
|
|
if (previous) {
|
|
glActiveTexture(GL_TEXTURE1);
|
|
glBindTexture(GL_TEXTURE_2D, shader->previous_texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 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 (!uses_gl()) return;
|
|
GLint major = 0, minor = 0;
|
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
|
|
|
if (major * 0x100 + minor < 0x302) {
|
|
return;
|
|
}
|
|
|
|
glDeleteProgram(shader->program);
|
|
glDeleteTextures(1, &shader->texture);
|
|
glDeleteTextures(1, &shader->previous_texture);
|
|
|
|
}
|