[GTK3] Use frame buffering like the Cocoa frontend
This commit is contained in:
parent
4d4d272a5c
commit
6e65945c35
183
gtk3/main.c
183
gtk3/main.c
@ -10,22 +10,6 @@
|
|||||||
#define str(x) #x
|
#define str(x) #x
|
||||||
#define xstr(x) str(x)
|
#define xstr(x) str(x)
|
||||||
|
|
||||||
GtkBuilder *builder;
|
|
||||||
GtkWindow *main_window;
|
|
||||||
GtkGLArea *gl_area;
|
|
||||||
shader_t shader;
|
|
||||||
|
|
||||||
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;
|
||||||
@ -38,8 +22,43 @@ typedef struct{
|
|||||||
|
|
||||||
static void run(UserData *user_data);
|
static void run(UserData *user_data);
|
||||||
|
|
||||||
|
GtkBuilder *builder;
|
||||||
|
GtkApplicationWindow *main_window;
|
||||||
|
GtkGLArea *gl_area;
|
||||||
|
shader_t shader;
|
||||||
|
|
||||||
|
GB_gameboy_t gb;
|
||||||
|
static uint32_t *image_buffers[3];
|
||||||
|
static unsigned char current_buffer;
|
||||||
|
|
||||||
|
static bool paused = false;
|
||||||
|
static bool underclock_down = false, rewind_down = false, do_rewind = false, rewind_paused = false, turbo_down = false;
|
||||||
|
static double clock_mutliplier = 1.0;
|
||||||
|
static char *battery_save_path_ptr;
|
||||||
static Rect rect;
|
static Rect rect;
|
||||||
|
|
||||||
|
unsigned char number_of_buffers(void) {
|
||||||
|
bool should_blend = true;
|
||||||
|
|
||||||
|
return should_blend? 3 : 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flip(void) {
|
||||||
|
current_buffer = (current_buffer + 1) % number_of_buffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t *get_pixels(void) {
|
||||||
|
return image_buffers[(current_buffer + 1) % number_of_buffers()];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t *get_current_buffer(void) {
|
||||||
|
return image_buffers[current_buffer];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t *get_previous_buffer(void) {
|
||||||
|
return image_buffers[(current_buffer + 2) % number_of_buffers()];
|
||||||
|
}
|
||||||
|
|
||||||
void render_texture(void *pixels, void *previous) {
|
void render_texture(void *pixels, void *previous) {
|
||||||
static void *_pixels = NULL;
|
static void *_pixels = NULL;
|
||||||
if (pixels) {
|
if (pixels) {
|
||||||
@ -50,7 +69,18 @@ void render_texture(void *pixels, void *previous) {
|
|||||||
glClear(GL_COLOR_BUFFER_BIT);
|
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);
|
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));
|
}
|
||||||
|
|
||||||
|
static void vblank(GB_gameboy_t *gb) {
|
||||||
|
flip();
|
||||||
|
GB_set_pixels_output(gb, get_pixels());
|
||||||
|
|
||||||
|
// Queue drawing of the current frame
|
||||||
|
gtk_gl_area_queue_render(gl_area);
|
||||||
|
|
||||||
|
while (gtk_events_pending()) {
|
||||||
|
gtk_main_iteration();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_viewport(void) {
|
void update_viewport(void) {
|
||||||
@ -168,30 +198,9 @@ 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) {
|
G_MODULE_EXPORT void gl_init() {
|
||||||
render_texture(active_pixel_buffer, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
G_MODULE_EXPORT void gl_init(GtkWidget *window) {
|
|
||||||
const char *renderer;
|
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);
|
gtk_gl_area_make_current(gl_area);
|
||||||
if (gtk_gl_area_get_error(gl_area) != NULL) {
|
if (gtk_gl_area_get_error(gl_area) != NULL) {
|
||||||
return;
|
return;
|
||||||
@ -205,6 +214,16 @@ G_MODULE_EXPORT void gl_init(GtkWidget *window) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
G_MODULE_EXPORT void gl_resize() {
|
||||||
|
update_viewport();
|
||||||
|
}
|
||||||
|
|
||||||
|
G_MODULE_EXPORT void gl_draw() {
|
||||||
|
render_texture(get_current_buffer(), get_previous_buffer());
|
||||||
|
}
|
||||||
|
|
||||||
|
G_MODULE_EXPORT void gl_finish() { }
|
||||||
|
|
||||||
// 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;
|
||||||
@ -220,24 +239,29 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
|
|||||||
GtkWindow *vram_viewer = GTK_WINDOW(get_object("vram_viewer"));
|
GtkWindow *vram_viewer = GTK_WINDOW(get_object("vram_viewer"));
|
||||||
set_combo_box_row_separator_func(GTK_CONTAINER(vram_viewer));
|
set_combo_box_row_separator_func(GTK_CONTAINER(vram_viewer));
|
||||||
|
|
||||||
|
// setup main window
|
||||||
|
main_window = GTK_APPLICATION_WINDOW(gtk_application_window_new(GTK_APPLICATION(app)));
|
||||||
|
gtk_application_window_set_show_menubar(main_window, true);
|
||||||
|
|
||||||
|
// create our renderer area
|
||||||
|
gl_area = GTK_GL_AREA(gtk_gl_area_new());
|
||||||
|
gtk_gl_area_set_auto_render(gl_area, false);
|
||||||
|
g_signal_connect(gl_area, "realize", G_CALLBACK(gl_init), NULL);
|
||||||
|
g_signal_connect(gl_area, "render", G_CALLBACK(gl_draw), NULL);
|
||||||
|
g_signal_connect(gl_area, "resize", G_CALLBACK(gl_resize), NULL);
|
||||||
|
g_signal_connect(gl_area, "unrealize", G_CALLBACK(gl_finish), NULL);
|
||||||
|
gtk_container_add(GTK_CONTAINER(main_window), GTK_WIDGET(gl_area));
|
||||||
|
|
||||||
// Handle the whole menubar situation …
|
// Handle the whole menubar situation …
|
||||||
if (show_menubar()) {
|
if (show_menubar()) {
|
||||||
// Use a standard window as main window
|
// Show a classic menubar
|
||||||
main_window = GTK_WINDOW(get_object("main_no_titlebar"));
|
|
||||||
gtk_widget_destroy(GTK_WIDGET(get_object("main_with_titlebar")));
|
|
||||||
|
|
||||||
// Hide hamburger button
|
|
||||||
GtkMenuButton *hamburger_button = GTK_MENU_BUTTON(get_object("hamburger_button"));
|
|
||||||
gtk_widget_hide(GTK_WIDGET(hamburger_button));
|
|
||||||
gtk_widget_set_no_show_all(GTK_WIDGET(hamburger_button), TRUE);
|
|
||||||
|
|
||||||
GMenuModel *menubar = get_menu_model(app, "menubar");
|
GMenuModel *menubar = get_menu_model(app, "menubar");
|
||||||
gtk_application_set_menubar(GTK_APPLICATION(app), menubar);
|
gtk_application_set_menubar(GTK_APPLICATION(app), menubar);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Use a window with a custom title bar
|
// Attach a custom title bar
|
||||||
main_window = GTK_WINDOW(get_object("main_with_titlebar"));
|
GtkWidget *titlebar = GTK_WIDGET(gtk_builder_get_object(builder, "main_header_bar"));
|
||||||
gtk_widget_destroy(GTK_WIDGET(get_object("main_no_titlebar")));
|
gtk_window_set_titlebar(GTK_WINDOW(main_window), titlebar);
|
||||||
|
|
||||||
// Disable menubar
|
// Disable menubar
|
||||||
gtk_application_set_menubar(GTK_APPLICATION(app), NULL);
|
gtk_application_set_menubar(GTK_APPLICATION(app), NULL);
|
||||||
@ -248,7 +272,7 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
|
|||||||
gtk_menu_button_set_menu_model(hamburger_button, hamburger_menu);
|
gtk_menu_button_set_menu_model(hamburger_button, hamburger_menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_window_set_title(main_window, "SameBoy v" xstr(VERSION));
|
gtk_window_set_title(GTK_WINDOW(main_window), "SameBoy v" xstr(VERSION));
|
||||||
|
|
||||||
// Define a set of window icons
|
// Define a set of window icons
|
||||||
GList *icon_list = NULL;
|
GList *icon_list = NULL;
|
||||||
@ -270,7 +294,7 @@ static void startup(GApplication *app, gpointer user_data_gptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let GTK choose the proper icon
|
// Let GTK choose the proper icon
|
||||||
gtk_window_set_icon_list(main_window, icon_list);
|
gtk_window_set_icon_list(GTK_WINDOW(main_window), icon_list);
|
||||||
|
|
||||||
// Add missing information to the about dialog
|
// Add missing information to the about dialog
|
||||||
GtkAboutDialog *about_dialog = GTK_ABOUT_DIALOG(get_object("about_dialog"));
|
GtkAboutDialog *about_dialog = GTK_ABOUT_DIALOG(get_object("about_dialog"));
|
||||||
@ -284,14 +308,13 @@ static void activate(GApplication *app, gpointer user_data_gptr) {
|
|||||||
UserData *user_data = user_data_gptr;
|
UserData *user_data = user_data_gptr;
|
||||||
|
|
||||||
if (user_data->fullscreen) {
|
if (user_data->fullscreen) {
|
||||||
gtk_window_fullscreen(main_window);
|
gtk_window_fullscreen(GTK_WINDOW(main_window));
|
||||||
}
|
}
|
||||||
|
|
||||||
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), GTK_WINDOW(main_window));
|
||||||
gtk_widget_show_all(GTK_WIDGET(main_window));
|
gtk_widget_show_all(GTK_WIDGET(main_window));
|
||||||
|
|
||||||
update_viewport();
|
|
||||||
run(user_data);
|
run(user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,25 +357,57 @@ static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) {
|
|||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vblank(GB_gameboy_t *gb) {
|
static void update_window_geometry() {
|
||||||
// render_texture(active_pixel_buffer, NULL);
|
// Set size hints
|
||||||
|
GdkGeometry hints;
|
||||||
|
hints.min_width = GB_get_screen_width(&gb);
|
||||||
|
hints.min_height = GB_get_screen_height(&gb);
|
||||||
|
|
||||||
while (gtk_events_pending()) {
|
gtk_window_set_geometry_hints(
|
||||||
gtk_main_iteration();
|
GTK_WINDOW(main_window),
|
||||||
}
|
NULL,
|
||||||
|
&hints,
|
||||||
|
(GdkWindowHints)(GDK_HINT_MIN_SIZE)
|
||||||
|
);
|
||||||
|
|
||||||
|
gtk_window_resize(GTK_WINDOW(main_window),
|
||||||
|
GB_get_screen_width(&gb) * 2,
|
||||||
|
GB_get_screen_height(&gb) * 2
|
||||||
|
);
|
||||||
|
|
||||||
|
if (image_buffers[0]) free(image_buffers[0]);
|
||||||
|
if (image_buffers[1]) free(image_buffers[1]);
|
||||||
|
if (image_buffers[2]) free(image_buffers[2]);
|
||||||
|
|
||||||
|
size_t buffer_size = sizeof(image_buffers[0][0]) * GB_get_screen_width(&gb) * GB_get_screen_height(&gb);
|
||||||
|
|
||||||
|
image_buffers[0] = malloc(buffer_size);
|
||||||
|
image_buffers[1] = malloc(buffer_size);
|
||||||
|
image_buffers[2] = malloc(buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run(UserData *user_data) {
|
static void run(UserData *user_data) {
|
||||||
|
GB_model_t prev_model = GB_get_model(&gb);
|
||||||
GB_model_t model = GB_MODEL_CGB_E;
|
GB_model_t model = GB_MODEL_CGB_E;
|
||||||
|
|
||||||
if (GB_is_inited(&gb)) {
|
if (GB_is_inited(&gb)) {
|
||||||
GB_switch_model_and_reset(&gb, model);
|
GB_switch_model_and_reset(&gb, model);
|
||||||
|
|
||||||
|
GtkRequisition minimum_size;
|
||||||
|
GtkRequisition natural_size;
|
||||||
|
gtk_widget_get_preferred_size(GTK_WIDGET(main_window), &minimum_size, &natural_size);
|
||||||
|
|
||||||
|
// Check SGB -> non-SGB and non-SGB to SGB transitions
|
||||||
|
if (GB_get_screen_width(&gb) != minimum_size.width || GB_get_screen_height(&gb) != minimum_size.height) {
|
||||||
|
update_window_geometry();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
GB_init(&gb, model);
|
GB_init(&gb, model);
|
||||||
|
update_window_geometry();
|
||||||
|
|
||||||
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
GB_set_vblank_callback(&gb, (GB_vblank_callback_t) vblank);
|
||||||
GB_set_pixels_output(&gb, active_pixel_buffer);
|
GB_set_pixels_output(&gb, get_current_buffer());
|
||||||
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
GB_set_rgb_encode_callback(&gb, rgb_encode);
|
||||||
// GB_set_sample_rate(&gb, have_aspec.freq);
|
// GB_set_sample_rate(&gb, have_aspec.freq);
|
||||||
// GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
|
// GB_set_color_correction_mode(&gb, configuration.color_correction_mode);
|
||||||
@ -422,6 +477,10 @@ int main(int argc, char *argv[]) {
|
|||||||
g_signal_connect(app, "activate", G_CALLBACK(activate), &user_data);
|
g_signal_connect(app, "activate", G_CALLBACK(activate), &user_data);
|
||||||
g_signal_connect(app, "open", G_CALLBACK(open), &user_data);
|
g_signal_connect(app, "open", G_CALLBACK(open), &user_data);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
//gtk_window_set_interactive_debugging(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Start our GApplication main loop
|
// Start our GApplication main loop
|
||||||
int status = g_application_run(G_APPLICATION(app), argc, argv);
|
int status = g_application_run(G_APPLICATION(app), argc, argv);
|
||||||
g_object_unref(app);
|
g_object_unref(app);
|
||||||
|
@ -965,36 +965,7 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkApplicationWindow" id="main_no_titlebar">
|
<object class="GtkHeaderBar" id="main_header_bar">
|
||||||
<property name="app_paintable">True</property>
|
|
||||||
<property name="can_focus">False</property>
|
|
||||||
<property name="title" translatable="yes">SameBoy</property>
|
|
||||||
<property name="default_width">320</property>
|
|
||||||
<property name="default_height">288</property>
|
|
||||||
<child type="titlebar">
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkGLArea">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="app_paintable">True</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>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<object class="GtkApplicationWindow" id="main_with_titlebar">
|
|
||||||
<property name="app_paintable">True</property>
|
|
||||||
<property name="can_focus">True</property>
|
|
||||||
<property name="has_focus">True</property>
|
|
||||||
<property name="is_focus">True</property>
|
|
||||||
<property name="title" translatable="yes">SameBoy</property>
|
|
||||||
<property name="default_width">320</property>
|
|
||||||
<property name="default_height">288</property>
|
|
||||||
<child type="titlebar">
|
|
||||||
<object class="GtkHeaderBar">
|
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="has_subtitle">False</property>
|
<property name="has_subtitle">False</property>
|
||||||
@ -1014,18 +985,6 @@ Maximilian Mader https://github.com/max-m</property>
|
|||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkGLArea">
|
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="app_paintable">True</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>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
<object class="GtkWindow" id="memory_viewer">
|
<object class="GtkWindow" id="memory_viewer">
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="title" translatable="yes">Memory Viewer</property>
|
<property name="title" translatable="yes">Memory Viewer</property>
|
||||||
|
@ -75,7 +75,6 @@ bool init_shader_with_name(shader_t *shader, const char *name)
|
|||||||
GBytes *master_shader_f;
|
GBytes *master_shader_f;
|
||||||
static const gchar *master_shader_code;
|
static const gchar *master_shader_code;
|
||||||
static gsize master_shader_code_size;
|
static gsize master_shader_code_size;
|
||||||
|
|
||||||
static char final_shader_code[0x10801] = {0,};
|
static char final_shader_code[0x10801] = {0,};
|
||||||
static signed long filter_token_location = 0;
|
static signed long filter_token_location = 0;
|
||||||
|
|
||||||
@ -85,11 +84,14 @@ bool init_shader_with_name(shader_t *shader, const char *name)
|
|||||||
|
|
||||||
if (!master_shader_f) {
|
if (!master_shader_f) {
|
||||||
g_printerr("Failed to load master shader: %s", error->message);
|
g_printerr("Failed to load master shader: %s", error->message);
|
||||||
|
g_error_free(error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code;
|
filter_token_location = strstr(master_shader_code, "{filter}") - master_shader_code;
|
||||||
|
|
||||||
if (filter_token_location < 0) {
|
if (filter_token_location < 0) {
|
||||||
|
g_error_free(error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,6 +102,7 @@ bool init_shader_with_name(shader_t *shader, const char *name)
|
|||||||
GBytes *shader_f = g_resources_lookup_data(shader_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
|
GBytes *shader_f = g_resources_lookup_data(shader_path, G_RESOURCE_LOOKUP_FLAGS_NONE, &error);
|
||||||
if (!shader_f) {
|
if (!shader_f) {
|
||||||
g_printerr("Failed to load shader \"%s\": %s", shader_path, error->message);
|
g_printerr("Failed to load shader \"%s\": %s", shader_path, error->message);
|
||||||
|
g_error_free(error);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,6 +115,8 @@ bool init_shader_with_name(shader_t *shader, const char *name)
|
|||||||
strcat(final_shader_code + filter_token_location,
|
strcat(final_shader_code + filter_token_location,
|
||||||
master_shader_code + filter_token_location + sizeof("{filter}") - 1);
|
master_shader_code + filter_token_location + sizeof("{filter}") - 1);
|
||||||
|
|
||||||
|
g_bytes_unref(shader_f);
|
||||||
|
|
||||||
shader->program = create_program(vertex_shader, final_shader_code);
|
shader->program = create_program(vertex_shader, final_shader_code);
|
||||||
|
|
||||||
// Attributes
|
// Attributes
|
||||||
|
Loading…
Reference in New Issue
Block a user