From 045d0e6085bcab955185cb4273d6fc658b5eaef6 Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Mon, 13 Oct 2025 13:56:11 +0100 Subject: [PATCH] Create separate renderer and framebuffer modules --- build.sh | 5 +- fb.c | 49 +++++++++++++++++++ fb.h | 17 +++++++ main.c | 138 +++++------------------------------------------------ renderer.c | 112 +++++++++++++++++++++++++++++++++++++++++++ renderer.h | 19 ++++++++ 6 files changed, 212 insertions(+), 128 deletions(-) create mode 100644 fb.c create mode 100644 fb.h create mode 100644 renderer.c create mode 100644 renderer.h diff --git a/build.sh b/build.sh index 7ed6d33..c27767d 100755 --- a/build.sh +++ b/build.sh @@ -4,5 +4,8 @@ cc="gcc" warn="-std=c23 -pedantic -Wall -Wextra" opt="-O2 -march=native" libs="-I/usr/include/libdrm -ldrm" +defs="-D_POSIX_C_SOURCE=200809L" -$cc $flags $libs main.c -o asteroids +$cc $flags $libs $defs \ + -o asteroids \ + fb.c main.c renderer.c diff --git a/fb.c b/fb.c new file mode 100644 index 0000000..f1e6bf8 --- /dev/null +++ b/fb.c @@ -0,0 +1,49 @@ +#include "fb.h" + +#include +#include +#include + +fb_info_t fb_init(int drm_fd, drmModeModeInfo mode) +{ + int err; + + struct drm_mode_create_dumb create = { + .width = mode.hdisplay, + .height = mode.vdisplay, + .bpp = 32, + }; + err = ioctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); + assert(err != -1); + + uint32_t id; + err = drmModeAddFB( + drm_fd, create.width, create.height, 24, 32, create.pitch, + create.handle, &id); + assert(err == 0); + + struct drm_mode_map_dumb map = { .handle = create.handle }; + err = ioctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map); + assert(err != -1); + + uint32_t *buf = mmap( + 0, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, + map.offset); + assert(buf != MAP_FAILED); + + return (fb_info_t) { + .buf = buf, + .handle = create.handle, + .id = id, + .pitch = create.pitch, + .size = create.size, + }; +} + +void fb_cleanup(int drm_fd, fb_info_t fb) +{ + munmap(fb.buf, fb.size); + drmModeRmFB(drm_fd, fb.id); + struct drm_mode_destroy_dumb delete = { .handle = fb.handle }; + ioctl(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &delete); +} diff --git a/fb.h b/fb.h new file mode 100644 index 0000000..e6a5b97 --- /dev/null +++ b/fb.h @@ -0,0 +1,17 @@ +#ifndef FB_H +#define FB_H + +#include + +typedef struct { + uint32_t *buf; + uint32_t handle; + uint32_t id; + uint32_t pitch; + uint32_t size; +} fb_info_t; + +fb_info_t fb_init(int drm_fd, drmModeModeInfo mode); +void fb_cleanup(int drm_fd, fb_info_t fb); + +#endif diff --git a/main.c b/main.c index cf9f119..a7fa5bd 100644 --- a/main.c +++ b/main.c @@ -1,90 +1,13 @@ -#define _POSIX_C_SOURCE 200809L +#include "renderer.h" #include #include #include -#include -#include -#include -#include -#include -#include #include #include -#include -#include #define MAX(a, b) ((a) < (b) ? (b) : (a)) -typedef struct { - uint32_t *buf; - uint32_t handle; - uint32_t id; - uint32_t pitch; - uint32_t size; -} fb_info_t; - -static fb_info_t fbs[2]; -static int front = 0, back = 1; - -static fb_info_t fb_init(int drm_fd, drmModeModeInfo mode) -{ - int err; - - struct drm_mode_create_dumb create = { - .width = mode.hdisplay, - .height = mode.vdisplay, - .bpp = 32, - }; - err = ioctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); - assert(err != -1); - - uint32_t id; - err = drmModeAddFB( - drm_fd, create.width, create.height, 24, 32, create.pitch, - create.handle, &id); - assert(err == 0); - - struct drm_mode_map_dumb map = { .handle = create.handle }; - err = ioctl(drm_fd, DRM_IOCTL_MODE_MAP_DUMB, &map); - assert(err != -1); - - uint32_t *buf = mmap( - 0, create.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm_fd, - map.offset); - assert(buf != MAP_FAILED); - - return (fb_info_t) { - .buf = buf, - .handle = create.handle, - .id = id, - .pitch = create.pitch, - .size = create.size, - }; -} - -static void fb_cleanup(int drm_fd, fb_info_t fb) -{ - munmap(fb.buf, fb.size); - drmModeRmFB(drm_fd, fb.id); - struct drm_mode_destroy_dumb delete = { .handle = fb.handle }; - ioctl(drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &delete); -} - -static void page_flip_handler(int, unsigned, unsigned, unsigned, void *) -{ -} - -static void swap_buffers(int drm_fd, uint32_t crtc_id) -{ - const int err = drmModePageFlip( - drm_fd, crtc_id, fbs[back].id, DRM_MODE_PAGE_FLIP_EVENT, nullptr); - assert(err == 0); - - front = (front + 1) & 1; - back = (back + 1) & 1; -} - int main() { int err; @@ -92,36 +15,20 @@ int main() const int input_fd = open("/dev/input/event0", O_RDONLY); assert(input_fd != -1); - const int drm_fd = open("/dev/dri/card1", O_RDWR | O_CLOEXEC); - assert(drm_fd != -1); + const renderer_params_t renderer_params = renderer_init(); + const int drm_fd = renderer_params.drm_fd; - drmModeRes *res = drmModeGetResources(drm_fd); - assert(res != nullptr); - drmModeConnector *conn = drmModeGetConnector(drm_fd, res->connectors[0]); - assert(conn != nullptr); - drmModeModeInfo mode = conn->modes[0]; - uint32_t conn_id = conn->connector_id; - - fbs[0] = fb_init(drm_fd, mode); - fbs[1] = fb_init(drm_fd, mode); - - err = drmModeSetCrtc( - drm_fd, res->crtcs[0], fbs[front].id, 0, 0, &conn_id, 1, &mode); - assert(err == 0); - drmModeCrtc *crtc = drmModeGetCrtc(drm_fd, res->crtcs[0]); - assert(crtc != nullptr); - - memset(fbs[back].buf, 0, fbs[back].size); - swap_buffers(drm_fd, crtc->crtc_id); + renderer_clear(); + renderer_swap(); const int max_fd = MAX(input_fd, drm_fd); fd_set set; bool running = true; - uint64_t frame = 0; while (running) { FD_ZERO(&set); FD_SET(input_fd, &set); FD_SET(drm_fd, &set); + select(max_fd + 1, &set, nullptr, nullptr, nullptr); if (FD_ISSET(input_fd, &set)) { @@ -136,37 +43,14 @@ int main() } if (FD_ISSET(drm_fd, &set)) { - drmEventContext ev = { - .version = DRM_EVENT_CONTEXT_VERSION, - .page_flip_handler = page_flip_handler, - }; - drmHandleEvent(drm_fd, &ev); - - for (int y = 0; y < mode.vdisplay; ++y) { - for (int x = 0; x < mode.hdisplay; ++x) { - const uint8_t r = (x + frame) % 256; - const uint8_t g = (y + frame) % 256; - const uint8_t b = (x + y + frame) % 256; - fbs[back].buf[y * (fbs[back].pitch / 4) + x] - = (r << 16) | (g << 8) | b; - } - } - - swap_buffers(drm_fd, crtc->crtc_id); - ++frame; + renderer_handle(); + renderer_draw(); + renderer_swap(); } } - drmModeSetCrtc( - drm_fd, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, &conn_id, - 1, &crtc->mode); - - fb_cleanup(drm_fd, fbs[0]); - fb_cleanup(drm_fd, fbs[1]); - drmModeFreeCrtc(crtc); - drmModeFreeConnector(conn); - drmModeFreeResources(res); - close(drm_fd); + renderer_cleanup(); + close(input_fd); return 0; } diff --git a/renderer.c b/renderer.c new file mode 100644 index 0000000..bc8ceb1 --- /dev/null +++ b/renderer.c @@ -0,0 +1,112 @@ +#include "renderer.h" + +#include "fb.h" + +#include +#include +#include +#include +#include + +static int drm_fd; +static drmModeConnector *conn; +static drmModeCrtc *crtc; +static uint32_t conn_id; + +static fb_info_t fbs[2]; +static int front, back; + +static uint32_t width, height; +static void page_flip_handler(int, unsigned, unsigned, unsigned, void *) +{ +} + +static void +set_pixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) +{ + /* x %= width; */ + /* y %= height; */ + const uint32_t offset = y * (fbs[back].pitch / 4) + x; + assert(offset < fbs[back].size); + fbs[back].buf[offset] = (r << 16) | (g << 8) | b; +} + +renderer_params_t renderer_init() +{ + drm_fd = open("/dev/dri/card1", O_RDWR | O_CLOEXEC); + assert(drm_fd != -1); + + drmModeRes *res = drmModeGetResources(drm_fd); + assert(res != nullptr); + conn = drmModeGetConnector(drm_fd, res->connectors[0]); + assert(conn != nullptr); + drmModeModeInfo mode = conn->modes[0]; + conn_id = conn->connector_id; + + fbs[0] = fb_init(drm_fd, mode); + fbs[1] = fb_init(drm_fd, mode); + front = 0; + back = 1; + + const int err = drmModeSetCrtc( + drm_fd, res->crtcs[0], fbs[front].id, 0, 0, &conn_id, 1, &mode); + assert(err == 0); + crtc = drmModeGetCrtc(drm_fd, res->crtcs[0]); + assert(crtc != nullptr); + + drmModeFreeResources(res); + + width = mode.hdisplay; + height = mode.vdisplay; + + return (renderer_params_t) { + .drm_fd = drm_fd, + .aspect = (float)mode.hdisplay / (float)mode.vdisplay, + }; +} + +void renderer_cleanup() +{ + drmModeSetCrtc( + drm_fd, crtc->crtc_id, crtc->buffer_id, crtc->x, crtc->y, &conn_id, + 1, &crtc->mode); + + fb_cleanup(drm_fd, fbs[0]); + fb_cleanup(drm_fd, fbs[1]); + drmModeFreeCrtc(crtc); + drmModeFreeConnector(conn); + close(drm_fd); +} + +void renderer_handle() +{ + drmEventContext ev = { + .version = DRM_EVENT_CONTEXT_VERSION, + .page_flip_handler = page_flip_handler, + }; + drmHandleEvent(drm_fd, &ev); +} + +void renderer_clear() +{ + memset(fbs[back].buf, 0, fbs[back].size); +} + +void renderer_swap() +{ + const int err = drmModePageFlip( + drm_fd, crtc->crtc_id, fbs[back].id, DRM_MODE_PAGE_FLIP_EVENT, + nullptr); + assert(err == 0); + + front = (front + 1) & 1; + back = (back + 1) & 1; +} + +void renderer_draw() +{ + for (unsigned y = 0; y < height; ++y) { + for (unsigned x = 0; x < width; ++x) + set_pixel(x, y, (x * 255) / width, (y * 255) / height, 128); + } +} diff --git a/renderer.h b/renderer.h new file mode 100644 index 0000000..c562d79 --- /dev/null +++ b/renderer.h @@ -0,0 +1,19 @@ +#ifndef RENDERER_H +#define RENDERER_H + +#include + +typedef struct { + int drm_fd; + float aspect; +} renderer_params_t; + +renderer_params_t renderer_init(); +void renderer_cleanup(); + +void renderer_handle(); +void renderer_clear(); +void renderer_swap(); + +void renderer_draw(); +#endif