the frexel situation has improved significantly since the invention of upscaling

This commit is contained in:
zlago 2025-11-11 00:51:15 +01:00
parent 0f5e6d9a4d
commit 7ca0d351f7
5 changed files with 183 additions and 19 deletions

View File

@ -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} $<

View File

@ -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;
}

View File

@ -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<std::string, unsigned> programs;
std::unordered_map<std::string, struct texture> 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;
}
}

View File

@ -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);
}

View File

@ -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);
}