Create separate renderer and framebuffer modules

This commit is contained in:
2025-10-18 17:43:22 +01:00
parent 72d73c2889
commit d975f5ed42
6 changed files with 212 additions and 128 deletions

View File

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

49
fb.c Normal file
View File

@@ -0,0 +1,49 @@
#include "fb.h"
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
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);
}

17
fb.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef FB_H
#define FB_H
#include <xf86drmMode.h>
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

138
main.c
View File

@@ -1,90 +1,13 @@
#define _POSIX_C_SOURCE 200809L
#include "renderer.h"
#include <assert.h>
#include <fcntl.h>
#include <linux/input.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/select.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#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;
}

112
renderer.c Normal file
View File

@@ -0,0 +1,112 @@
#include "renderer.h"
#include "fb.h"
#include <assert.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <xf86drm.h>
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);
}
}

19
renderer.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef RENDERER_H
#define RENDERER_H
#include <stdint.h>
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