[GTK3] Implement OpenGL renderer prototype
This commit is contained in:
parent
6913c47076
commit
be64d422c5
7
Makefile
7
Makefile
@ -108,8 +108,11 @@ ifneq ($(findstring gtk3,$(MAKECMDGOALS)),)
|
|||||||
$(error The gtk3 target requires pkg-config)
|
$(error The gtk3 target requires pkg-config)
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
GTK3_CFLAGS := $(shell $(PKG_CONFIG) --cflags gtk+-3.0) -DGTK_DISABLE_DEPRECATED=1 -DG_DISABLE_DEPRECATED=1
|
GTK3_CFLAGS := $(shell $(PKG_CONFIG) --cflags gio-2.0 gtk+-3.0 epoxy) -DGTK_DISABLE_DEPRECATED=1 -DG_DISABLE_DEPRECATED=1 -DRESOURCE_PREFIX=\"/io/github/sameboy/\" -DAPP_ID=\"io.github.sameboy\"
|
||||||
GTK3_LDFLAGS := $(shell $(PKG_CONFIG) --libs gtk+-3.0)
|
GTK3_LDFLAGS := $(shell $(PKG_CONFIG) --libs gio-2.0 gtk+-3.0 epoxy)
|
||||||
|
|
||||||
|
# TODO: REMOVE DISABLE UNUSED WARNINGS
|
||||||
|
GTK3_CFLAGS += -Wno-unused
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifeq ($(PLATFORM),windows32)
|
ifeq ($(PLATFORM),windows32)
|
||||||
|
180
gtk3/main.c
180
gtk3/main.c
@ -1,27 +1,87 @@
|
|||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <epoxy/gl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <Core/gb.h>
|
#include <Core/gb.h>
|
||||||
|
#include "shader.h"
|
||||||
|
|
||||||
#define str(x) #x
|
#define str(x) #x
|
||||||
#define xstr(x) str(x)
|
#define xstr(x) str(x)
|
||||||
|
|
||||||
#define RESOURCE_PREFIX "/io/github/sameboy/"
|
|
||||||
#define APP_ID "io.github.sameboy"
|
|
||||||
|
|
||||||
GtkBuilder *builder;
|
GtkBuilder *builder;
|
||||||
GtkWindow *main_window;
|
GtkWindow *main_window;
|
||||||
GtkGLArea *gl_area;
|
GtkGLArea *gl_area;
|
||||||
|
shader_t shader;
|
||||||
|
|
||||||
GB_gameboy_t gb;
|
GB_gameboy_t gb;
|
||||||
|
static bool paused = false;
|
||||||
|
static uint32_t pixel_buffer_1[256 * 224], pixel_buffer_2[256 * 224];
|
||||||
|
static uint32_t *active_pixel_buffer = pixel_buffer_1, *previous_pixel_buffer = pixel_buffer_2;
|
||||||
|
static bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false;
|
||||||
|
static double clock_mutliplier = 1.0;
|
||||||
|
|
||||||
|
static typeof(free) *free_function = NULL;
|
||||||
|
static char *battery_save_path_ptr;
|
||||||
|
|
||||||
|
|
||||||
typedef struct UserData {
|
typedef struct UserData {
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
GFile *file;
|
GFile *file;
|
||||||
} UserData;
|
} UserData;
|
||||||
|
|
||||||
|
typedef struct{
|
||||||
|
int16_t x, y;
|
||||||
|
uint16_t w, h;
|
||||||
|
} Rect;
|
||||||
|
|
||||||
|
static void run(UserData *user_data);
|
||||||
|
|
||||||
|
static Rect rect;
|
||||||
|
|
||||||
|
void render_texture(void *pixels, void *previous) {
|
||||||
|
static void *_pixels = NULL;
|
||||||
|
if (pixels) {
|
||||||
|
_pixels = pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
glClearColor(0, 0, 0, 1);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
render_bitmap_with_shader(&shader, _pixels, previous, GB_get_screen_width(&gb), GB_get_screen_height(&gb), rect.x, rect.y, rect.w, rect.h);
|
||||||
|
gtk_widget_queue_draw(GTK_WIDGET(gl_area));
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_viewport(void) {
|
||||||
|
int win_width = gtk_widget_get_allocated_width(GTK_WIDGET(gl_area));
|
||||||
|
int win_height = gtk_widget_get_allocated_height(GTK_WIDGET(gl_area));
|
||||||
|
|
||||||
|
double x_factor = win_width / (double) GB_get_screen_width(&gb);
|
||||||
|
double y_factor = win_height / (double) GB_get_screen_height(&gb);
|
||||||
|
|
||||||
|
if (true /*configuration.scaling_mode == GB_SDL_SCALING_INTEGER_FACTOR*/) {
|
||||||
|
x_factor = (int)(x_factor);
|
||||||
|
y_factor = (int)(y_factor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*if (configuration.scaling_mode != GB_SDL_SCALING_ENTIRE_WINDOW) {
|
||||||
|
if (x_factor > y_factor) {
|
||||||
|
x_factor = y_factor;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
y_factor = x_factor;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
unsigned new_width = x_factor * GB_get_screen_width(&gb);
|
||||||
|
unsigned new_height = y_factor * GB_get_screen_height(&gb);
|
||||||
|
|
||||||
|
rect = (Rect){(win_width - new_width) / 2, (win_height - new_height) / 2, new_width, new_height};
|
||||||
|
|
||||||
|
glViewport(rect.x, rect.y, rect.w, rect.h);
|
||||||
|
}
|
||||||
|
|
||||||
// Determines if a ComboBox entry should be converted into a separator.
|
// Determines if a ComboBox entry should be converted into a separator.
|
||||||
// Each element with a text value of `<separator>` will be converted into a separator element.
|
// Each element with a text value of `<separator>` will be converted into a separator element.
|
||||||
static gboolean is_separator(GtkTreeModel *model, GtkTreeIter *iter, gpointer data) {
|
static gboolean is_separator(GtkTreeModel *model, GtkTreeIter *iter, gpointer data) {
|
||||||
@ -108,6 +168,43 @@ G_MODULE_EXPORT void on_boot_rom_location_changed(GtkWidget *w, gpointer user_da
|
|||||||
g_print("Active: %s", gtk_combo_box_get_active_id(box));
|
g_print("Active: %s", gtk_combo_box_get_active_id(box));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
G_MODULE_EXPORT void gl_draw(GtkWidget *window) {
|
||||||
|
render_texture(active_pixel_buffer, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_MODULE_EXPORT void gl_init(GtkWidget *window) {
|
||||||
|
const char *renderer;
|
||||||
|
|
||||||
|
GList *children = gtk_container_get_children(GTK_CONTAINER(window));
|
||||||
|
|
||||||
|
while (children) {
|
||||||
|
if (GTK_IS_GL_AREA(children->data)) {
|
||||||
|
gl_area = GTK_GL_AREA(children->data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
children = children->next;
|
||||||
|
}
|
||||||
|
g_list_free(children);
|
||||||
|
|
||||||
|
if (gl_area == NULL) {
|
||||||
|
g_printerr("Unable to find GtkGLArea in window\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_gl_area_make_current(gl_area);
|
||||||
|
if (gtk_gl_area_get_error(gl_area) != NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderer = (char *) glGetString(GL_RENDERER);
|
||||||
|
g_print("GtkGLArea on %s\n", renderer ? renderer : "Unknown");
|
||||||
|
|
||||||
|
if (!init_shader_with_name(&shader, /*configuration.filter*/ "OmniScale")) {
|
||||||
|
init_shader_with_name(&shader, "NearestNeighbor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This functions gets called immediately after registration of the GApplication
|
// This functions gets called immediately after registration of the GApplication
|
||||||
static void startup(GApplication *app, gpointer user_data_gptr) {
|
static void startup(GApplication *app, gpointer user_data_gptr) {
|
||||||
// UserData *user_data = user_data_gptr;
|
// UserData *user_data = user_data_gptr;
|
||||||
@ -180,10 +277,6 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
|
|||||||
gtk_about_dialog_set_logo(about_dialog, g_list_nth_data(icon_list, 3)); // reuse the 64x64 icon
|
gtk_about_dialog_set_logo(about_dialog, g_list_nth_data(icon_list, 3)); // reuse the 64x64 icon
|
||||||
gtk_about_dialog_set_version(about_dialog, "v" xstr(VERSION));
|
gtk_about_dialog_set_version(about_dialog, "v" xstr(VERSION));
|
||||||
g_list_free(icon_list);
|
g_list_free(icon_list);
|
||||||
|
|
||||||
GList *children = gtk_container_get_children(GTK_CONTAINER(main_window));
|
|
||||||
gl_area = GTK_GL_AREA(g_list_first(children));
|
|
||||||
g_list_free(children);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function gets called when the GApplication gets activated, i.e. it is ready to show widgets.
|
// This function gets called when the GApplication gets activated, i.e. it is ready to show widgets.
|
||||||
@ -197,6 +290,9 @@ static void activate(GApplication *app, gpointer user_data_gptr) {
|
|||||||
g_signal_connect(main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
|
g_signal_connect(main_window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
|
||||||
gtk_application_add_window(GTK_APPLICATION(app), main_window);
|
gtk_application_add_window(GTK_APPLICATION(app), main_window);
|
||||||
gtk_widget_show_all(GTK_WIDGET(main_window));
|
gtk_widget_show_all(GTK_WIDGET(main_window));
|
||||||
|
|
||||||
|
update_viewport();
|
||||||
|
run(user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function gets called when there are files to open.
|
// This function gets called when there are files to open.
|
||||||
@ -233,6 +329,76 @@ static gint handle_local_options(GApplication *app, GVariantDict *options, gpoin
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
uint32_t color = 0xFF000000 | (b << 16) | (g << 8) | r; // abgr
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vblank(GB_gameboy_t *gb) {
|
||||||
|
// render_texture(active_pixel_buffer, NULL);
|
||||||
|
|
||||||
|
while (gtk_events_pending()) {
|
||||||
|
gtk_main_iteration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void run(UserData *user_data) {
|
||||||
|
GB_model_t model = GB_MODEL_CGB_E;
|
||||||
|
|
||||||
|
if (GB_is_inited(&gb)) {
|
||||||
|
GB_switch_model_and_reset(&gb, model);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
GB_init(&gb, model);
|
||||||
|
|
||||||
|
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
||||||
|
GB_set_pixels_output(&gb, active_pixel_buffer);
|
||||||
|
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
||||||
|
// GB_set_sample_rate(&gb, have_aspec.freq);
|
||||||
|
// GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
|
||||||
|
// GB_set_highpass_filter_mode(&gb, configuration.highpass_mode);
|
||||||
|
// GB_set_rewind_length(&gb, configuration.rewind_length);
|
||||||
|
// GB_set_update_input_hint_callback(&gb, handle_events);
|
||||||
|
// GB_apu_set_sample_callback(&gb, gb_audio_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
GError *gerror;
|
||||||
|
GBytes *boot_rom_f;
|
||||||
|
const guchar *boot_rom_data;
|
||||||
|
gsize boot_rom_size;
|
||||||
|
|
||||||
|
boot_rom_f = g_resources_lookup_data(RESOURCE_PREFIX "bootroms/cgb_boot.bin", G_RESOURCE_LOOKUP_FLAGS_NONE, &gerror);
|
||||||
|
boot_rom_data = g_bytes_get_data(boot_rom_f, &boot_rom_size);
|
||||||
|
|
||||||
|
GB_load_boot_rom_from_buffer(&gb, boot_rom_data, boot_rom_size);
|
||||||
|
|
||||||
|
if (user_data->file != NULL) {
|
||||||
|
GB_load_rom(&gb, g_file_get_path(user_data->file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run emulation */
|
||||||
|
while (true) {
|
||||||
|
if (paused || rewind_paused) {
|
||||||
|
while (gtk_events_pending()) {
|
||||||
|
gtk_main_iteration();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (do_rewind) {
|
||||||
|
GB_rewind_pop(&gb);
|
||||||
|
if (turbo_down) {
|
||||||
|
GB_rewind_pop(&gb);
|
||||||
|
}
|
||||||
|
if (!GB_rewind_pop(&gb)) {
|
||||||
|
rewind_paused = true;
|
||||||
|
}
|
||||||
|
do_rewind = false;
|
||||||
|
}
|
||||||
|
GB_run(&gb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
// Create our GApplication and tell GTK that we are able to handle files
|
// Create our GApplication and tell GTK that we are able to handle files
|
||||||
GtkApplication *app = gtk_application_new(APP_ID, G_APPLICATION_HANDLES_OPEN);
|
GtkApplication *app = gtk_application_new(APP_ID, G_APPLICATION_HANDLES_OPEN);
|
||||||
|
@ -979,6 +979,9 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="app_paintable">True</property>
|
<property name="app_paintable">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<signal name="realize" handler="gl_init" object="main_no_titlebar" swapped="yes"/>
|
||||||
|
<signal name="render" handler="gl_draw" object="main_no_titlebar" swapped="yes"/>
|
||||||
|
<signal name="unrealize" handler="gl_finish" object="main_no_titlebar" swapped="yes"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
@ -1017,6 +1020,9 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="app_paintable">True</property>
|
<property name="app_paintable">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
|
<signal name="realize" handler="gl_init" object="main_with_titlebar" swapped="yes"/>
|
||||||
|
<signal name="render" handler="gl_draw" object="main_with_titlebar" swapped="yes"/>
|
||||||
|
<signal name="unrealize" handler="gl_finish" object="main_with_titlebar" swapped="yes"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
206
gtk3/shader.c
Normal file
206
gtk3/shader.c
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
#include <glib.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
#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 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_printerr("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_printerr("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)
|
||||||
|
{
|
||||||
|
GLint major = 0, minor = 0;
|
||||||
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||||
|
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
||||||
|
|
||||||
|
if (major * 0x100 + minor < 0x302) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GError *error;
|
||||||
|
GBytes *master_shader_f;
|
||||||
|
static const gchar *master_shader_code;
|
||||||
|
static gsize master_shader_code_size;
|
||||||
|
|
||||||
|
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_printerr("Failed to load master shader: %s", error->message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code;
|
||||||
|
if (filter_token_location < 0) {
|
||||||
|
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_printerr("Failed to load shader \"%s\": %s", shader_path, error->message);
|
||||||
|
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);
|
||||||
|
|
||||||
|
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->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_RGBA, source_width, source_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 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_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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
25
gtk3/shader.h
Normal file
25
gtk3/shader.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef shader_h
|
||||||
|
#define shader_h
|
||||||
|
#include <epoxy/gl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct shader_s {
|
||||||
|
GLuint resolution_uniform;
|
||||||
|
GLuint origin_uniform;
|
||||||
|
GLuint texture_uniform;
|
||||||
|
GLuint previous_texture_uniform;
|
||||||
|
GLuint mix_previous_uniform;
|
||||||
|
|
||||||
|
GLuint position_attribute;
|
||||||
|
GLuint texture;
|
||||||
|
GLuint previous_texture;
|
||||||
|
GLuint program;
|
||||||
|
} shader_t;
|
||||||
|
|
||||||
|
bool init_shader_with_name(shader_t *shader, const char *name);
|
||||||
|
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);
|
||||||
|
void free_shader(struct shader_s *shader);
|
||||||
|
|
||||||
|
#endif /* shader_h */
|
Loading…
Reference in New Issue
Block a user