From 7ca0d351f7e0603fb798f2c6ab0488838a9b0929 Mon Sep 17 00:00:00 2001 From: zlago <104219492+zlago@users.noreply.github.com> Date: Tue, 11 Nov 2025 00:51:15 +0100 Subject: [PATCH] the frexel situation has improved significantly since the invention of upscaling --- GNUmakefile | 2 +- src/default.glsl | 2 +- src/main.c++ | 183 +++++++++++++++++++++++++++++++++++++++++++-- src/up.normal.frag | 3 +- src/up.trans.frag | 12 +-- 5 files changed, 183 insertions(+), 19 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index ec2a42f..917a8b8 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -34,7 +34,7 @@ clean: ${RM} -r ${ns}/ ${ns}/${NAME}.${EXTENSION}: ${objs} - ${LD} ${ldflags} -o $@ $^ + ${LD} -o $@ $^ ${ldflags} ${ns}/%.o: src/%.c | ${ns}/ ${CC} ${cflags} -c -o $@ -MMD -MP -MF ${@:.o=.d} $< diff --git a/src/default.glsl b/src/default.glsl index bac754b..3b4f235 100644 --- a/src/default.glsl +++ b/src/default.glsl @@ -23,7 +23,7 @@ void main(void) { uniform sampler2D uTex; void main(void) { - vec4 Color = texture2D(uTex, vTexCoord) + vec4 Color = texture2D(uTex, vTexCoord); if (Color.a < 0.5) { discard; } diff --git a/src/main.c++ b/src/main.c++ index 9618317..e7d1806 100644 --- a/src/main.c++ +++ b/src/main.c++ @@ -13,15 +13,23 @@ #include "assets.h++" #include "main.h++" -#define WINDOW_WIDTH 128 -#define WINDOW_HEIGHT 128 +#define WINDOW_VIRTUAL 128 // screen buffer, ideally with some padding for the offscreen +#define WINDOW_WIDTH 128 // viewport width +#define WINDOW_HEIGHT 128 // viewport height +#define WINDOW_DENOM 128 // highest common denominator of width and height, for square pixel purposes #define BATCH_SIZE 1024 SDL_Window *window; +int16_t window_width = WINDOW_WIDTH, window_height = WINDOW_HEIGHT; SDL_GLContext context; std::unordered_map programs; std::unordered_map textures; +GLuint framebuffer = 0; + +bool square_pixels = true, integer_upscale = false; + +static int framebuffer_init(void); SDL_AppResult SDL_AppInit(void **, int argc, char **argv) { clock_t begin = clock(); @@ -33,7 +41,7 @@ SDL_AppResult SDL_AppInit(void **, int argc, char **argv) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "60"); - window = SDL_CreateWindow("suwi", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL /*| SDL_WINDOW_RESIZABLE*/ | SDL_WINDOW_TRANSPARENT); + window = SDL_CreateWindow("suwi", WINDOW_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_TRANSPARENT); SDL_SetWindowMinimumSize(window, WINDOW_WIDTH, WINDOW_HEIGHT); context = SDL_GL_CreateContext(window); @@ -75,7 +83,8 @@ SDL_AppResult SDL_AppInit(void **, int argc, char **argv) { batch_init(BATCH_SIZE, programs.at("spr2D")); - glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); + framebuffer_init(); + SDL_ShowWindow(window); fprintf(stderr, "info: init took %lu ms\n", ((clock() - begin) * 1000) / CLOCKS_PER_SEC); @@ -90,6 +99,13 @@ SDL_AppResult SDL_AppEvent(void *, SDL_Event *evt) { case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: + if (!evt->key.repeat && evt->key.down == true) { + if (evt->key.scancode == SDL_SCANCODE_1) { + square_pixels = !square_pixels; + } else if (evt->key.scancode == SDL_SCANCODE_2) { + integer_upscale = !integer_upscale; + } + } break; case SDL_EVENT_MOUSE_MOTION: @@ -104,13 +120,21 @@ SDL_AppResult SDL_AppEvent(void *, SDL_Event *evt) { case SDL_EVENT_WINDOW_MAXIMIZED: case SDL_EVENT_WINDOW_RESTORED: case SDL_EVENT_WINDOW_MOVED: + break; case SDL_EVENT_WINDOW_RESIZED: + window_width = evt->window.data1; + window_height = evt->window.data2; + break; case SDL_EVENT_WINDOW_MOUSE_ENTER: case SDL_EVENT_WINDOW_MOUSE_LEAVE: case SDL_EVENT_WINDOW_FOCUS_GAINED: case SDL_EVENT_WINDOW_FOCUS_LOST: case SDL_EVENT_WINDOW_CLOSE_REQUESTED: break; + case SDL_EVENT_WINDOW_EXPOSED: + case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: + case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: + break; case SDL_EVENT_CLIPBOARD_UPDATE: break; @@ -122,14 +146,66 @@ SDL_AppResult SDL_AppEvent(void *, SDL_Event *evt) { return SDL_APP_CONTINUE; } +static struct { + GLuint vbo, program; + GLint aVertCoord, aTexCoord, uTex, uBackGround, uForeGround; + GLuint color, depth, stencil; +} fb; + SDL_AppResult SDL_AppIterate(void *) { - glClearColor(0.5, 0.5, 0.5, 0.0); - glClear(GL_COLOR_BUFFER_BIT); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + glViewport(0, 0, WINDOW_VIRTUAL, WINDOW_VIRTUAL); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); struct crop c = {0, 0, 64, 64}; batch_blit(-32, -32, &textures.at("turret"), &c); batch_flush(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + if (square_pixels == false && integer_upscale == false) { + glViewport(0, 0, window_width, window_height); + } else if (square_pixels == false && integer_upscale == true) { + unsigned w = window_width / WINDOW_WIDTH * WINDOW_WIDTH, h = window_height / WINDOW_HEIGHT * WINDOW_HEIGHT; + glViewport((window_width - w) / 2, (window_height - h) / 2, w, h); + } else if (square_pixels == true && integer_upscale == false) { + static_assert(WINDOW_WIDTH / WINDOW_DENOM * WINDOW_DENOM == WINDOW_WIDTH); + static_assert(WINDOW_HEIGHT / WINDOW_DENOM * WINDOW_DENOM == WINDOW_HEIGHT); + unsigned w = window_width * WINDOW_DENOM / WINDOW_WIDTH, h = window_height * WINDOW_DENOM / WINDOW_HEIGHT; + unsigned s = w > h? h: w; + w = s * WINDOW_WIDTH / WINDOW_DENOM, h = s * WINDOW_HEIGHT / WINDOW_DENOM; + glViewport((window_width - w) / 2, (window_height - h) / 2, w, h); + } else if (square_pixels == true && integer_upscale == true) { + unsigned w = window_width / WINDOW_WIDTH, h = window_height / WINDOW_HEIGHT; + unsigned s = w > h? h: w; + w = s * WINDOW_WIDTH, h = s * WINDOW_HEIGHT; + glViewport((window_width - w) / 2, (window_height - h) / 2, w, h); + } else { + // how + } + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + glBindBuffer(GL_ARRAY_BUFFER, fb.vbo); + glUseProgram(fb.program); + + glVertexAttribPointer(fb.aVertCoord, 2, GL_FLOAT, GL_FALSE, sizeof (float [4]), (void *) 0); + glVertexAttribPointer(fb.aTexCoord, 2, GL_FLOAT, GL_TRUE, sizeof (float [4]), (void *) sizeof (float [2])); + + glEnableVertexAttribArray(fb.aVertCoord); + glEnableVertexAttribArray(fb.aTexCoord); + + glUniform4f(fb.uForeGround, 1, 1, 0, 1); + glUniform4f(fb.uBackGround, 0, 0, 1, 1); + + glUniform1i(fb.uTex, 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, fb.color); + glDrawArrays(GL_TRIANGLES, 0, 6); + + glDisableVertexAttribArray(fb.aVertCoord); + glDisableVertexAttribArray(fb.aTexCoord); + SDL_GL_SwapWindow(window); return SDL_APP_CONTINUE; } @@ -137,3 +213,98 @@ SDL_AppResult SDL_AppIterate(void *) { void SDL_AppQuit(void *, SDL_AppResult) { ; } + +static int framebuffer_init(void) { + fb.program = programs.at("up.trans"); + glGenBuffers(1, &fb.vbo); + + fb.aVertCoord = glGetAttribLocation(fb.program, "aVertCoord"); + fb.aTexCoord = glGetAttribLocation(fb.program, "aTexCoord"); + fb.uTex = glGetUniformLocation(fb.program, "uTex"); + fb.uBackGround = glGetUniformLocation(fb.program, "uBackGround"); + fb.uForeGround = glGetUniformLocation(fb.program, "uForeGround"); + + static_assert(WINDOW_WIDTH <= WINDOW_VIRTUAL); + static_assert(WINDOW_HEIGHT <= WINDOW_VIRTUAL); + const float horizontal = (WINDOW_VIRTUAL - WINDOW_WIDTH) / 2.0 / WINDOW_VIRTUAL; + const float vertical = (WINDOW_VIRTUAL - WINDOW_HEIGHT) / 2.0 / WINDOW_VIRTUAL; + const float vertices[] = { + -1, -1, 0 + horizontal, 0 + vertical, + +1, -1, 1 - horizontal, 0 + vertical, + -1, +1, 0 + horizontal, 1 - vertical, + +1, -1, 1 - horizontal, 0 + vertical, + -1, +1, 0 + horizontal, 1 - vertical, + +1, +1, 1 - horizontal, 1 - vertical, + }; + glBindBuffer(GL_ARRAY_BUFFER, fb.vbo); + glBufferData(GL_ARRAY_BUFFER, 6 * sizeof (float [4]), vertices, GL_STATIC_DRAW); + + glGenFramebuffers(1, &framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); + + glGenTextures(1, &fb.color); + glBindTexture(GL_TEXTURE_2D, fb.color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, WINDOW_VIRTUAL, WINDOW_VIRTUAL, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb.color, 0); + + glGenRenderbuffers(1, &fb.depth); + glBindRenderbuffer(GL_RENDERBUFFER, fb.depth); + + while (glGetError() != GL_NO_ERROR); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, WINDOW_VIRTUAL, WINDOW_VIRTUAL); + + // too much hope in humanity? dont worry! + // GLES2 does not support a depth+stencil format + // GLES2 has extensions for D24S8 format + // WEBGL supports a depth+stencil format + // the problem? the two are DIFFERENT depth+stencil formats! + if (glGetError() == GL_NO_ERROR) { + // depth24_stencil8 supported + fprintf(stderr, "info: fb: D24S8\n"); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.depth); + } else { + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, WINDOW_VIRTUAL, WINDOW_VIRTUAL); + if (glGetError() == GL_NO_ERROR) { + // depth_stencil supported (??) + fprintf(stderr, "info: fb: DS?\n"); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.depth); + } else { + // neither, fallback to separate depth and stencil + fprintf(stderr, "info: fb: D16 S8\n"); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, WINDOW_VIRTUAL, WINDOW_VIRTUAL); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, fb.depth); + + glGenRenderbuffers(1, &fb.stencil); + glBindRenderbuffer(GL_RENDERBUFFER, fb.stencil); + glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, WINDOW_VIRTUAL, WINDOW_VIRTUAL); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, fb.stencil); + } + } + + switch (glCheckFramebufferStatus(GL_FRAMEBUFFER)) { + case GL_FRAMEBUFFER_COMPLETE: + return 0; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + fprintf(stderr, "error: fb: incomplete attachment\n"); + return 1; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + fprintf(stderr, "error: fb: missing attachment\n"); + return 1; + case GL_FRAMEBUFFER_UNSUPPORTED: + fprintf(stderr, "error: fb: unsupported\n"); + return 1; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + fprintf(stderr, "error: fb: samples dont match\n"); + return 1; + case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: + fprintf(stderr, "error: fb: layers\n"); + return 1; + default: + fprintf(stderr, "error: fb: unknown error\n"); + return 1; + } +} diff --git a/src/up.normal.frag b/src/up.normal.frag index 16d3c8d..a656174 100644 --- a/src/up.normal.frag +++ b/src/up.normal.frag @@ -7,9 +7,8 @@ varying vec2 vTexCoord; uniform sampler2D uTex; uniform vec4 uForeGround; uniform vec4 uBackGround; -uniform vec4 uBackDrop; void main(void) { float Color = texture2D(uTex, vTexCoord).r; - gl_FragColor = mix(uBg, uFg, Color); + gl_FragColor = mix(uBackGround, uForeGround, Color); } diff --git a/src/up.trans.frag b/src/up.trans.frag index 471d5ae..15369d4 100644 --- a/src/up.trans.frag +++ b/src/up.trans.frag @@ -7,17 +7,11 @@ varying vec2 vTexCoord; uniform sampler2D uTex; uniform vec4 uForeGround; uniform vec4 uBackGround; -uniform vec4 uBackDrop; void main(void) { - vec3 d = vec3(-1.0, 0.0, +1.0) / 256.0; - float Color = texture2D(uTex, vTexCoord).r; - float ColorL = texture2D(uTex, vTexCoord + d.xy).r; - float ColorR = texture2D(uTex, vTexCoord + dzy).r; - float ColorU = texture2D(uTex, vTexCoord + d.yx).r; - float ColorD = texture2D(uTex, vTexCoord + d.yz).r; - if (Color + ColorL + ColorR + ColorU + ColorD < 0.5) { + vec4 Color = texture2D(uTex, vTexCoord); + if (Color.a < 0.5) { discard; } - gl_FragColor = mix(uBg, uFg, Color); + gl_FragColor = mix(uBackGround, uForeGround, Color.r); }