From 3d41defaf9e815fcba1f4e0dc64a905386593be6 Mon Sep 17 00:00:00 2001 From: DaniTheSkunk <> Date: Sat, 14 Jan 2023 04:48:58 +0000 Subject: [PATCH] implemented scalers --- examples/gsa_simple.c | 2 +- include/framebuffer.h | 6 +++++ include/scaler.h | 8 ++++++ include/shader.h | 2 ++ include/shaders.h | 11 ++++++++ include/types.h | 3 +++ include/vec2i.h | 6 +++-- include/window.h | 4 +-- meson.build | 2 ++ src/framebuffer.c | 61 +++++++++++------------------------------- src/scaler.c | 62 +++++++++++++++++++++++++++++++++++++++++++ src/shader.c | 2 ++ src/shaders.c | 53 ++++++++++++++++++++++++++++++++++++ src/types.c | 8 ++++++ src/window.c | 35 +++++++++++++++++++++--- 15 files changed, 211 insertions(+), 54 deletions(-) create mode 100644 include/shaders.h create mode 100644 src/scaler.c create mode 100644 src/shaders.c diff --git a/examples/gsa_simple.c b/examples/gsa_simple.c index 721b7f9..5886bfd 100644 --- a/examples/gsa_simple.c +++ b/examples/gsa_simple.c @@ -14,6 +14,6 @@ void init() { void tick() { sprites[0].x += 1; - sprites[1].x += 2; + sprites[1].x += 0; maps[0].scrollx += 1; } diff --git a/include/framebuffer.h b/include/framebuffer.h index 9f10a49..92b214a 100644 --- a/include/framebuffer.h +++ b/include/framebuffer.h @@ -9,12 +9,18 @@ struct sw_framebuffer { u32 _fb, _fbtex, _fbdepth; }; +extern u32 sw_screen_vao; + struct sw_framebuffer *sw_framebuffer_create(struct sw_vec2i size); void sw_framebuffer_destroy(struct sw_framebuffer *fb); void sw_framebuffer_static_init(); +/* pass 0 to use currently bound */ void sw_framebuffer_copy_to_screen(struct sw_framebuffer *fb); +/* pass 0 to use currently bound */ +void sw_framebuffer_render(struct sw_framebuffer *fb); + void sw_framebuffer_resize(struct sw_framebuffer *fb, struct sw_vec2i new_size); #endif /* GUARD_4770FE04EA612B028DF68960A453BD0B */ diff --git a/include/scaler.h b/include/scaler.h index c481798..e39cced 100644 --- a/include/scaler.h +++ b/include/scaler.h @@ -1,8 +1,16 @@ #ifndef GUARD_D3B2B8EAB7F3D9A513B3C06FAAF6357E #define GUARD_D3B2B8EAB7F3D9A513B3C06FAAF6357E +#include "types.h" +#include "vec2i.h" + #define SW_SCALE_STRETCH 0 #define SW_SCALE_ASPECT 1 #define SW_SCALE_INTEGER 2 +/* input texture and output framebuffer(unless screen) need to be bound */ +void sw_scaler_apply( + i32 scaler, struct sw_vec2i in_size, struct sw_vec2i out_size +); + #endif /* GUARD_D3B2B8EAB7F3D9A513B3C06FAAF6357E */ diff --git a/include/shader.h b/include/shader.h index e48b7a9..e979aba 100644 --- a/include/shader.h +++ b/include/shader.h @@ -14,6 +14,8 @@ struct sw_shaderprogram { #define SW_SHADER_VERTEX 1 #define SW_SHADER_FRAGMENT 2 +#define SW_SHADERLOC_POS 0 + struct sw_shader sw_shader_create(char const *source, i32 type); struct sw_shaderprogram diff --git a/include/shaders.h b/include/shaders.h new file mode 100644 index 0000000..6b798c4 --- /dev/null +++ b/include/shaders.h @@ -0,0 +1,11 @@ +#ifndef GUARD_1B77D3247B24AE540CDA7D1880E34980 +#define GUARD_1B77D3247B24AE540CDA7D1880E34980 + +#include "shader.h" + +extern struct sw_shaderprogram sw_shaders_copy; +extern struct sw_shaderprogram sw_shaders_scale; + +void sw_shaders_static_init(); + +#endif /* GUARD_1B77D3247B24AE540CDA7D1880E34980 */ diff --git a/include/types.h b/include/types.h index f8e3fab..26b7b04 100644 --- a/include/types.h +++ b/include/types.h @@ -19,4 +19,7 @@ typedef i8 bool; i32 i32_max(i32 v1, i32 v2); i32 i32_min(i32 v1, i32 v2); +f32 f32_max(f32 v1, f32 v2); +f32 f32_min(f32 v1, f32 v2); + #endif /* GUARD_649E63003BEAE79ECA2B813C4E131C9A */ diff --git a/include/vec2i.h b/include/vec2i.h index 72516ea..4743a4f 100644 --- a/include/vec2i.h +++ b/include/vec2i.h @@ -1,10 +1,12 @@ #ifndef GUARD_B329567D1B1FDC855E0489217F4BCBE2 #define GUARD_B329567D1B1FDC855E0489217F4BCBE2 +#include "types.h" + struct sw_vec2i { - int x, y; + i32 x, y; }; -struct sw_vec2i sw_vec2i(int x, int y); +struct sw_vec2i sw_vec2i(i32 x, i32 y); #endif /* GUARD_B329567D1B1FDC855E0489217F4BCBE2 */ diff --git a/include/window.h b/include/window.h index 349132d..369a09b 100644 --- a/include/window.h +++ b/include/window.h @@ -8,8 +8,8 @@ struct GLFWwindow; struct sw_window { - struct sw_vec2i size; - struct sw_vec2i real_size; + struct sw_vec2i game_size; + struct sw_vec2i window_size; i32 scaler; struct GLFWwindow *_window; diff --git a/meson.build b/meson.build index 5061726..ab52642 100644 --- a/meson.build +++ b/meson.build @@ -47,7 +47,9 @@ skunk_sources = [ 'src/gsa.c', 'src/image32.c', 'src/image8.c', + 'src/scaler.c', 'src/shader.c', + 'src/shaders.c', 'src/skunkworks.c', 'src/types.c', 'src/vec2i.c', diff --git a/src/framebuffer.c b/src/framebuffer.c index ddcb6b7..62291c2 100644 --- a/src/framebuffer.c +++ b/src/framebuffer.c @@ -2,34 +2,12 @@ #include "error.h" #include "gl.h" #include "shader.h" +#include "shaders.h" #include -static struct sw_shaderprogram program_copy; - -static char *vert_copy = "#version 300 es\n\ -precision highp float;\ -in vec2 pos;\ -out vec2 out_tex_coord;\ -void main() {\ - gl_Position = vec4(pos, 0.0f, 1.0f);\ - out_tex_coord = vec2(pos.x * 0.5 + 0.5, pos.y * 0.5 + 0.5);\ -}"; - -static char *frag_copy = "#version 300 es\n\ -precision highp float;\ -in vec2 out_tex_coord;\ -uniform sampler2D tex;\ -out vec4 color;\ -void main() {\ - vec4 col = texture(tex, out_tex_coord);\ -if(col.a > 0.0) {\ - color = col;\ -}\ -}"; - static f32 screen_rect[12] = { -1.f, -1.f, 1.f, -1.f, -1.f, 1.f, 1.f, -1.f, -1.f, 1.f, 1.f, 1.f}; -static u32 vao, vbo; +static u32 sw_screen_vao, vbo; struct sw_framebuffer *sw_framebuffer_create(struct sw_vec2i size) { struct sw_framebuffer *fb; @@ -50,13 +28,9 @@ void sw_framebuffer_destroy(struct sw_framebuffer *fb) { } void sw_framebuffer_static_init() { - sw_log("init static framebuffer"); - - program_copy = sw_shaderprogram_create(vert_copy, frag_copy); - sw_log("init static framebuffer vao"); - glGenVertexArrays(1, &vao); - glBindVertexArray(vao); + glGenVertexArrays(1, &sw_screen_vao); + glBindVertexArray(sw_screen_vao); sw_log("init static framebuffer vbo"); glGenBuffers(1, &vbo); @@ -64,30 +38,27 @@ void sw_framebuffer_static_init() { glBufferData( GL_ARRAY_BUFFER, sizeof(f32) * 12, screen_rect, GL_STATIC_DRAW ); - glGetAttribLocation(program_copy._program, "pos"); - glVertexAttribPointer( - glGetAttribLocation(program_copy._program, "pos"), - 2, - GL_FLOAT, - GL_FALSE, - 0, - 0 - ); + glVertexAttribPointer(SW_SHADERLOC_POS, 2, GL_FLOAT, GL_FALSE, 0, 0); - glEnableVertexAttribArray(glGetAttribLocation(program_copy._program, "pos") - ); + glEnableVertexAttribArray(SW_SHADERLOC_POS); glBindVertexArray(0); sw_log("init static framebuffer done"); } void sw_framebuffer_copy_to_screen(struct sw_framebuffer *fb) { - glBindVertexArray(vao); - sw_shaderprogram_use(program_copy); - glBindTexture(GL_TEXTURE_2D, fb->_fbtex); glBindFramebuffer(GL_FRAMEBUFFER, 0); + sw_shaderprogram_use(sw_shaders_copy); + sw_framebuffer_render(fb); +} - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +void sw_framebuffer_render(struct sw_framebuffer *fb) { + glBindVertexArray(sw_screen_vao); + if(fb) { + glBindTexture(GL_TEXTURE_2D, fb->_fbtex); + } + + glClear(GL_DEPTH_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, 12); glBindVertexArray(0); diff --git a/src/scaler.c b/src/scaler.c new file mode 100644 index 0000000..d50ddb5 --- /dev/null +++ b/src/scaler.c @@ -0,0 +1,62 @@ +#include "scaler.h" +#include "error.h" +#include "framebuffer.h" +#include "gl.h" +#include "shader.h" +#include "shaders.h" + +void sw_scaler_apply( + i32 scaler, struct sw_vec2i in_size, struct sw_vec2i out_size +) { + (void)in_size; + (void)out_size; + f32 scalex, scaley, scale, xoff, yoff; + + switch(scaler) { + case SW_SCALE_STRETCH: + sw_shaderprogram_use(sw_shaders_copy); + break; + case SW_SCALE_ASPECT: + sw_shaderprogram_use(sw_shaders_scale); + scalex = (f32)out_size.x / (f32)in_size.x; + scaley = (f32)out_size.y / (f32)in_size.y; + scale = f32_min(scalex, scaley); + + xoff = (out_size.x - in_size.x * scale) / 2 / out_size.x; + yoff = (out_size.y - in_size.y * scale) / 2 / out_size.y; + glUniform2f( + glGetUniformLocation(sw_shaders_scale._program, "offset"), + xoff, + yoff + ); + glUniform2f( + glGetUniformLocation(sw_shaders_scale._program, "size_ratio"), + (f32)out_size.x / (f32)in_size.x / scale, + (f32)out_size.y / (f32)in_size.y / scale + ); + break; + case SW_SCALE_INTEGER: + sw_shaderprogram_use(sw_shaders_scale); + scalex = (f32)out_size.x / (f32)in_size.x; + scaley = (f32)out_size.y / (f32)in_size.y; + scale = f32_min(scalex, scaley); + scale = i32_max(1, (i32)scale); + + xoff = (out_size.x - in_size.x * scale) / 2 / out_size.x; + yoff = (out_size.y - in_size.y * scale) / 2 / out_size.y; + glUniform2f( + glGetUniformLocation(sw_shaders_scale._program, "offset"), + xoff, + yoff + ); + glUniform2f( + glGetUniformLocation(sw_shaders_scale._program, "size_ratio"), + (f32)out_size.x / (f32)in_size.x / scale, + (f32)out_size.y / (f32)in_size.y / scale + ); + break; + default: + sw_error("unknown scaler %i", scaler); + } + sw_framebuffer_render(0); +} diff --git a/src/shader.c b/src/shader.c index 9fc340a..49668e8 100644 --- a/src/shader.c +++ b/src/shader.c @@ -54,6 +54,8 @@ sw_shaderprogram_create(char const *vertex, char const *fragment) { program._program = glCreateProgram(); glAttachShader(program._program, v._shader); glAttachShader(program._program, f._shader); + sw_log("set attribute locations"); + glBindAttribLocation(program._program, SW_SHADERLOC_POS, "pos"); sw_log("link shader"); glLinkProgram(program._program); glGetProgramiv(program._program, GL_LINK_STATUS, &ret); diff --git a/src/shaders.c b/src/shaders.c new file mode 100644 index 0000000..4526e14 --- /dev/null +++ b/src/shaders.c @@ -0,0 +1,53 @@ +#include "shaders.h" +#include "error.h" + +struct sw_shaderprogram sw_shaders_copy; +struct sw_shaderprogram sw_shaders_scale; + +static char *vert_copy = "#version 300 es\n\ +precision highp float;\ +in vec2 pos;\ +out vec2 out_tex_coord;\ +void main() {\ + gl_Position = vec4(pos, 0.0f, 1.0f);\ + out_tex_coord = vec2(pos.x * 0.5 + 0.5, pos.y * 0.5 + 0.5);\ +}"; + +static char *frag_copy = "#version 300 es\n\ +precision highp float;\ +in vec2 out_tex_coord;\ +uniform sampler2D tex;\ +out vec4 color;\ +void main() {\ + vec4 col = texture(tex, out_tex_coord);\ +if(col.a > 0.0) {\ + color = col;\ +}\ +}"; + +static char *frag_scale = "#version 300 es\n\ +precision highp float;\ +in vec2 out_tex_coord;\ +uniform sampler2D tex;\ +uniform float scale;\ +uniform vec2 offset;\ +uniform vec2 size_ratio;\ +out vec4 color;\ +void main() {\ + vec2 tc = (out_tex_coord-offset)*size_ratio;\ + vec4 col = vec4(0,0,0,0);\ + if(tc.x <= 1.0 && tc.y <= 1.0 && tc.x >= 0.0 && tc.y >= 0.0) {\ + col = texture(tex, tc);\ + }\ + if(col.a > 0.0) {\ + color = col;\ + } else { \ + discard;\ + }\ +}"; + +void sw_shaders_static_init() { + sw_log("initialising shaders"); + sw_shaders_copy = sw_shaderprogram_create(vert_copy, frag_copy); + sw_shaders_scale = sw_shaderprogram_create(vert_copy, frag_scale); +} diff --git a/src/types.c b/src/types.c index 93c4a1c..00463f8 100644 --- a/src/types.c +++ b/src/types.c @@ -7,3 +7,11 @@ i32 i32_max(i32 v1, i32 v2) { i32 i32_min(i32 v1, i32 v2) { return v1 < v2 ? v1 : v2; } + +f32 f32_max(f32 v1, f32 v2) { + return v1 > v2 ? v1 : v2; +} + +f32 f32_min(f32 v1, f32 v2) { + return v1 < v2 ? v1 : v2; +} diff --git a/src/window.c b/src/window.c index 6efc476..57b78ab 100644 --- a/src/window.c +++ b/src/window.c @@ -1,14 +1,20 @@ #include "window.h" +#include "gl.h" +#include #include +#include "GLFW/glfw3.h" #include "error.h" #include "framebuffer.h" -#include "gl.h" +#include "scaler.h" +#include "shaders.h" +#include "vec2i.h" static void _tick(); static struct sw_window *_tick_window; static void (*_tick_fn)(); +static void win_callback(GLFWwindow *glfw_win, i32 width, i32 height); static void GLAPIENTRY gldebug( GLenum source, GLenum type, @@ -23,13 +29,15 @@ struct sw_window *sw_window_create(struct sw_vec2i size, char *title) { struct sw_window *win; win = malloc(sizeof(struct sw_window)); - win->size = size; + win->window_size = size; + win->game_size = size; sw_log("init glfw"); glfwInit(); glfwDefaultWindowHints(); win->_window = glfwCreateWindow(size.x, size.y, title, 0, 0); + glfwSetWindowUserPointer(win->_window, win); glfwShowWindow(win->_window); glfwMakeContextCurrent(win->_window); glewInit(); @@ -41,9 +49,12 @@ struct sw_window *sw_window_create(struct sw_vec2i size, char *title) { glEnable(GL_BLEND); glEnable(GL_DEPTH_TEST); + sw_shaders_static_init(); sw_framebuffer_static_init(); /* TODO: move elsewhere */ win->_scaler_fb = sw_framebuffer_create(size); + glfwSetWindowSizeCallback(win->_window, win_callback); + glClearColor(0.1f, 0.2f, 0.3f, 1.f); sw_log("window setup complete"); return win; @@ -65,15 +76,31 @@ void sw_window_run(struct sw_window *window, void (*callback)()) { /* private functions */ void _tick() { - glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, _tick_window->game_size.x, _tick_window->game_size.y); glBindFramebuffer(GL_FRAMEBUFFER, _tick_window->_scaler_fb->_fb); _tick_fn(); + + glViewport(0, 0, _tick_window->window_size.x, _tick_window->window_size.y); glBindFramebuffer(GL_FRAMEBUFFER, 0); - sw_framebuffer_copy_to_screen(_tick_window->_scaler_fb); + glBindTexture(GL_TEXTURE_2D, _tick_window->_scaler_fb->_fbtex); + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + sw_scaler_apply( + SW_SCALE_INTEGER, _tick_window->game_size, _tick_window->window_size + ); glfwSwapBuffers(_tick_window->_window); glfwPollEvents(); } +static void win_callback(GLFWwindow *glfw_win, i32 width, i32 height) { + struct sw_window *win; + + sw_log("resizing window to %ix%i", width, height); + + win = glfwGetWindowUserPointer(glfw_win); + win->window_size = sw_vec2i(width, height); +} + void GLAPIENTRY gldebug( GLenum source, GLenum type,