rendering: Added generic framebuffer
authorLukas Krickl <lukas@krickl.dev>
Tue, 24 Feb 2026 06:57:19 +0000 (07:57 +0100)
committerLukas Krickl <lukas@krickl.dev>
Tue, 24 Feb 2026 06:57:19 +0000 (07:57 +0100)
This can be used to render a frame to the screen.
The setup of a framebuffer is done by the rendering backend.
It should provide direct access to pixel data.

12 files changed:
src/config.h
src/lrts.c
src/p_draw.h
src/p_platform.h
src/p_r_sdl/p_draw.c
src/p_r_sdl/p_init.c
src/r_color.c
src/r_color.h
src/r_render.c
src/r_render.h
src/u_defs.h
src/u_math.h

index cdb422600f69ebba6e549ac70899b13d3134cbe0..14d320163ac7d08242e9b988b2b634db366cd6ed 100644 (file)
@@ -12,6 +12,9 @@
 /* Platform and renderer configuration */
 #define LRTS_PLATFORM_POSIX
 #define LRTS_RENDERER_SDL
+
+/* Defines target color as 32 bit */
+#define LRTS_COLOR_DEPTH_32
 /* #define LRTS_RENDERER_CLI */
 
 #endif
index f38a9fae8c3455208f123679ea193829f636ebba..01149c092c49ccc57bba082bbe5ca30072738405 100644 (file)
@@ -25,11 +25,31 @@ void lrts_version(void) {
 }
 
 
+/**
+ * The default render size
+ */
+#define U_RENDER_W 640
+#define U_RENDER_H 640
+
+struct u_vec2 lrts_get_render_res(void) {
+       struct lrts_config *cfg = lrts_cfg();
+
+       struct u_vec2 render_res = cfg->render_res;
+       if (render_res.x == 0) {
+               render_res.x = U_RENDER_W;
+       }
+       if (render_res.y == 0) {
+               render_res.y = U_RENDER_H;
+       }
+       return render_res;
+}
+
 int lrts_main(int argc, char **argv) {
        p_io_init();
   lrts_getopt(argc, argv, lrts_cfg());
        
        p_render_init();
+       p_render_init_framebuffer();
 
        while (!lrts_cfg()->exit) {
                p_poll_events();
index c1f73e3986df0860dffc76abd658bc2b81e00a67..12ea20625e26f448565bd3ba2c22a55cd336d9d3 100644 (file)
@@ -5,11 +5,6 @@
 #include "r_color.h"
 
 
-/* draws a singel pixel directly to the screen */
-void p_draw_pixel(i32 x, i32 y, r_color color);
-
-/* clears the current screen buffer */
-void p_draw_clear(void);
 
 /* inits the draw of a frame */
 void p_draw_begin(void);
index dee05941d50a4bad4d1b19a37166eda5803052c8..5de02c7d81a80ea3683dec89defe4813d043f819 100644 (file)
@@ -22,6 +22,11 @@ int p_io_finish(void);
  */
 int p_render_init(void);
 
+/*
+ * Sets up the frame buffer
+ */
+int p_render_init_framebuffer(void);
+
 /**
  * Quits rendering
  */
index d4196c5d3f3dfd321d8fd8250d2c423ce24e98ce..c465e42a726a22ad5412b830f7f7ba15cfc1991e 100644 (file)
@@ -3,27 +3,11 @@
 #include "../u_assert.h"
 
 void p_draw_begin(void) {
+       SDL_LockSurface(r_target);
 }
 
 void p_draw_end(void) {
-}
-
-void p_draw_clear(void) {
-       SDL_FillSurfaceRect(r_target, NULL, 0x000000FF);
-}
-
-void p_draw_pixel(i32 x, i32 y, r_color color) {
-       u32 *pixel = LRTS_NULL;
-       /* drop the draw if out of bounds */
-       if (x < 0 || x > r_target->w) {
-               return;
-       }
-       if (y < 0 || y > r_target->h) {
-               return;
-       }
-
- pixel = (unsigned int*)(((char*)r_target->pixels) + r_target->pitch * y + x);
- *pixel = color;
+       SDL_UnlockSurface(r_target);
 }
 
 void p_draw_present(void) {
index 35b32853962e6b79f7e52f2b56b8266a9365132f..51de98d7ebdb4490f1ca678ee1aa8811eba80488 100644 (file)
@@ -3,29 +3,44 @@
 #include "p_window.h"
 #include "../u_defs.h"
 #include "../u_log.h"
+#include "../u_math.h"
 
 #include "p_window.c"
 
 int p_render_init(void) {
+       struct lrts_config *cfg = lrts_cfg();
+       struct u_vec2 target_res = lrts_get_render_res();
+
        if (!SDL_Init(SDL_INIT_VIDEO)) {
                u_log(U_LOG_ERR, "Failed to init video: %s\n", 
                                SDL_GetError());
                exit(-1);
        }
 
-       p_main_window = SDL_CreateWindow(lrts_cfg()->name, R_WIDTH, R_HEIGHT, 0);
+       /* TODO: allow scaled resolution */
+       p_main_window = SDL_CreateWindow(cfg->name, target_res.x, target_res.y, 0);
        if (p_main_window == LRTS_NULL) {
                u_log(U_LOG_ERR, "Failed to create window: %s\n", SDL_GetError());
                exit(-1);
        }
 
-       r_target = SDL_CreateSurface(R_WIDTH, R_HEIGHT, SDL_PIXELFORMAT_RGBX8888);
+
+       u_log(U_LOG_DEBUG, "Init successful\n");
+
+       return 0;
+}
+
+int p_render_init_framebuffer(void) {
+       struct u_vec2 target_res = lrts_get_render_res();
+       r_target = SDL_CreateSurface(target_res.x, target_res.y, SDL_PIXELFORMAT_RGBX8888);
        if (r_target == LRTS_NULL) {
                u_log(U_LOG_ERR, "Failed to create surface: %s\n", SDL_GetError());
                exit(-1);
        }
 
-       u_log(U_LOG_DEBUG, "Init successful\n");
+       r_framebuffer.pixels = r_target->pixels;
+       r_framebuffer.width = r_target->w;
+       r_framebuffer.height = r_target->h;
 
        return 0;
 }
index 8b137891791fe96927ad78e64b0aad7bded08bdc..661d5d80ad6527bdc5ab7f73496225e7d44cb434 100644 (file)
@@ -1 +1,7 @@
+#include "r_color.h"
 
+#ifdef LRTS_COLOR_DEPTH_32
+r_target_color r_color_to_target(r_color c) {
+       return c;
+}
+#endif
index 3a56e481248ef02ce6bb366630ee433a827b0dd5..0fee111d7777f2f7b02f6b8591bf7f367532294d 100644 (file)
@@ -5,4 +5,18 @@
 
 typedef u32 r_color;
 
+/*
+ * Define a render target color
+ * This is here to allow changng the color depth
+ * at compile time
+ */
+
+#ifdef LRTS_COLOR_DEPTH_32
+typedef u32 r_target_color;
+#endif
+
+
+/** converts r_color to target color **/
+r_target_color r_color_to_target(r_color c);
+
 #endif
index 58c0972d1c25381f5fd6baba67bac25cad99c650..d53c8b72ada126dacb17bbb43ceb598cea2bc170 100644 (file)
@@ -2,11 +2,31 @@
 #include "r_color.h"
 #include "p_draw.h"
 
+
+struct r_framebuffer r_framebuffer;
+
+void r_draw_pixel(struct r_framebuffer *fb, i32 x, i32 y, r_color color) {
+       r_target_color *pixel = LRTS_NULL;
+
+       /* drop the draw if out of bounds */
+       if (x < 0 || x >= r_target->w) {
+               return;
+       }
+       if (y < 0 || y >= r_target->h) {
+               return;
+       }
+
+ pixel = fb->pixels + fb->width * y + x;
+ *pixel = color;
+}
+
 void r_render_frame() {
        p_draw_begin();
-       p_draw_clear();
 
-       p_draw_pixel(0, 0, 0xFF0000FF);
+       r_draw_pixel(&r_framebuffer, 0, 0, 0xFF0000FF);
+       r_draw_pixel(&r_framebuffer, 1, 1, 0xFF0000FF);
+       r_draw_pixel(&r_framebuffer, 2, 2, 0xFF0000FF);
+       r_draw_pixel(&r_framebuffer, r_framebuffer.width-1, r_framebuffer.height-1, 0xFF0000FF);
 
        p_draw_end();
        p_draw_present();
index 4cf94bbfa1a4bf6de3d6ba40f1d51991e99778d3..c36390aa863932ef2944dd42a2d074787ed0bec4 100644 (file)
@@ -1,9 +1,26 @@
 #ifndef R_RENDER_H__
 #define R_RENDER_H__
 
-/* internal rendering resolution */
-#define R_WIDTH 800
-#define R_HEIGHT 600
+#include "r_color.h"
+
+/** 
+ * A simple framebuffer 
+ * containing color data
+ */
+struct r_framebuffer {
+       u32 width;
+       u32 height;
+       r_target_color *pixels;
+};
+
+/* The global frame buffer. 
+ * This needs to be initialized by the 
+ * rendering platform
+ */
+extern struct r_framebuffer r_framebuffer;
+
+/* draws a singel pixel directly to a frame buffer */
+void r_draw_pixel(struct r_framebuffer *fb, i32 x, i32 y, r_color color);
 
 void r_render_frame();
 
index 25b62df74953bbb9755570475af30042abf75f18..f6f30fc877b45ac75622dfd458097860d370e923 100644 (file)
@@ -31,12 +31,16 @@ typedef unsigned char lrts_bool;
 #define LRTS_TRUE 1
 #define LRTS_FALSE 0
 
+#include "u_math.h"
+
 struct lrts_config {
        lrts_bool verbose;
 
        i8 exit;
 
        const char *name;
+       
+       struct u_vec2 render_res;
 
        char **argv;
        int argc;
@@ -49,8 +53,11 @@ void lrts_version(void);
 void lrts_getopt(int argc, char **argv, struct lrts_config *cfg);
 int lrts_main(int argc, char **argv);
 
-#define U_RENDER_W 640
-#define U_RENDER_H 640
+/**
+ * Returns the current render size as a vec2
+ */
+struct u_vec2 lrts_get_render_res(void);
+
 
 #define LRTS_UNUSED(x) (void)(x)
 
index fd7ebf035659c99b51322ac2de60bc2d07645741..543c97b1bea95f0fc54b968d6e13d5ad42dfd750 100644 (file)
@@ -1,7 +1,10 @@
 #ifndef U_MATH_H__
 #define U_MATH_H__
 
+#include "u_defs.h"
+
 #define U_FP(n, f) {n, f}
+#define U_V2(x, y) {x, y}
 
 /* fixed point number 
  * n: whole part
@@ -12,6 +15,22 @@ struct u_fp {
        u16 f;
 };
 
+/*
+ * A simple 2d vector
+ */
+struct u_vec2 {
+       u32 x;
+       u32 y;
+};
+
+/*
+ * A fixed point vector
+ */
+struct u_vecfp {
+       struct u_fp x;
+       struct u_fp y;
+};
+
 struct u_fp u_fp_add(struct u_fp a, struct u_fp b);
 struct u_fp u_fp_sub(struct u_fp a, struct u_fp b);
 struct u_fp u_fp_mul(struct u_fp a, struct u_fp b);