#include "renderer.h" #include "fb.h" #include #include #include #include #include #include float aspect; 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 mat3_t view; static bool wrap; static vec3_t vert_buf[MAX_VERTS]; static void page_flip_handler(int, unsigned, unsigned, unsigned, void *) { } static void set_pixel(uint32_t x, uint32_t y) { const uint32_t offset = y * (fbs[back].pitch / 4) + x; fbs[back].buf[offset] = 0xff'ff'ff; } static void draw_line(vec3_t v1, vec3_t v2) { v1.x /= v1.z; v1.y /= v1.z; v1.z = 1; v2.x /= v2.z; v2.y /= v2.z; v2.z = 1; const float delta_x = v2.x - v1.x; const float delta_y = v2.y - v1.y; const float step = fmaxf(fabsf(delta_x), fabsf(delta_y)); if (step != 0.0) { const float step_x = delta_x / step; const float step_y = delta_y / step; for (unsigned i = 0; i <= step; ++i) { float x = roundf(v1.x + i * step_x); float y = roundf(v1.y + i * step_y); if (wrap) { if (x >= width) x -= width; else if (x < 0) x += width; if (y >= height) y -= height; else if (y < 0) y += height; } else if (x >= width || x < 0 || y >= height || y < 0) { continue; } set_pixel(x, y); } } else { set_pixel(roundf(v1.x), roundf(v1.y)); } } int 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; wrap = true; aspect = (float)width / (float)height; const float scale = (float)height / 2.0f; view = (mat3_t) { { scale, 0, 0 }, { 0, -scale, 0 }, { aspect * scale, scale, 1 }, }; return drm_fd; } 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_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_set_wrap(bool enable) { wrap = enable; } void renderer_clear() { memset(fbs[back].buf, 0, fbs[back].size); } void renderer_clear_rect(vec2_t centre, vec2_t size) { const vec2_t tl_world = { centre.x - size.x / 2, centre.y + size.y / 2 }; const vec2_t br_world = { centre.x + size.x / 2, centre.y - size.y / 2 }; const vec3_t tl = mat3_mul_vec3(view, vec2_extend(tl_world)); const vec3_t br = mat3_mul_vec3(view, vec2_extend(br_world)); const unsigned width = (unsigned)roundf(br.x / br.z - tl.x / tl.z); const unsigned x1 = (unsigned)roundf(tl.x / tl.z); const unsigned y1 = (unsigned)roundf(tl.y / tl.z); const unsigned y2 = (unsigned)roundf(br.y / br.z); for (unsigned y = y1; y < y2; ++y) { uint32_t *row = fbs[back].buf + y * (fbs[back].pitch / 4) + x1; memset(row, 0, width * sizeof(uint32_t)); } } void renderer_draw( const vec2_t *vs, unsigned count, mat3_t model, bool connect) { mat3_t transform = mat3_mul_mat3(view, model); assert(count < MAX_VERTS); for (unsigned i = 0; i < count; ++i) vert_buf[i] = mat3_mul_vec3(transform, vec2_extend(vs[i])); for (unsigned i = 1; i < count; ++i) draw_line(vert_buf[i - 1], vert_buf[i]); if (connect) draw_line(vert_buf[count - 1], vert_buf[0]); }