diff --git a/Makefile b/Makefile index dfb9014..b9fd7b8 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,13 @@ CC = gcc INCFLAGS = -Isrc -CCFLAGS = -std=c99 -Wall -Wextra -pedantic -O2 +CCFLAGS = -std=gnu99 -Wall -Wextra -pedantic -O2 CCFLAGS += $(INCFLAGS) LDFLAGS += $(INCFLAGS) LDFLAGS += -lX11 LDFLAGS += -lm +LDFLAGS += -lpthread BIN = bin APP = $(BIN)/app diff --git a/src/renderer.c b/src/renderer.c index a77f855..e59631c 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -3,6 +3,8 @@ #include "ray.h" #include +#include +#include #include @@ -80,10 +82,11 @@ void update_camera(Camera* camera, Screen* screen) { if (movey) camera->pos.y += move.y * MOVE_SPEED * screen->delta; } -static void verline(Screen* screen, int x, int y0, int y1, uint32_t color) { +static void verline(Swapchain* swapchain, int x, int y0, int y1, uint32_t color) { for (int y = y0; y <= y1; y++) { - screen->pixels[(y * screen->width) + x] = color; + swapchain->images[swapchain->image_current][(y * swapchain->width) + x] = color; } + } void render(Screen* screen, const Camera* camera) { @@ -93,9 +96,13 @@ void render(Screen* screen, const Camera* camera) { cos(camera->angle) }; - for (int x = 0; x < screen->width; x++) { + Swapchain* swapchain = &screen->swapchain; - const float xcam = (2 * (x / (float) (screen->width))) - 1; + swapchain_next(swapchain); + + for (int x = 0; x < screen->swapchain.width; x++) { + + const float xcam = (2 * (x / (float) (swapchain->width))) - 1; const float change = fov * atan(xcam); const float theta = camera->angle + change; @@ -126,15 +133,18 @@ void render(Screen* screen, const Camera* camera) { // } const int - h = (int) (screen->height / len), - y0 = max((screen->height / 2) - (h / 2), 0), - y1 = min((screen->height / 2) + (h / 2), screen->height - 1); + h = (int) (swapchain->height / len), + y0 = max((swapchain->height / 2) - (h / 2), 0), + y1 = min((swapchain->height / 2) + (h / 2), swapchain->height - 1); + + verline(swapchain, x, 0, y0, 0xFF202020); + verline(swapchain, x, y0, y1, color); + verline(swapchain, x, y1, swapchain->height - 1, 0xFF505050); - verline(screen, x, 0, y0, 0xFF202020); - verline(screen, x, y0, y1, color); - verline(screen, x, y1, screen->height - 1, 0xFF505050); // verline(screen, x, 0, y0, 0x202020FF); // verline(screen, x, y0, y1, color); // verline(screen, x, y1, screen->height - 1, 0x505050FF); } + + swapchain_submit(swapchain); } diff --git a/src/screen.c b/src/screen.c index 762ecbc..eb2de87 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1,4 +1,5 @@ #include "screen.h" +#include #undef Screen #include @@ -7,6 +8,8 @@ #include #include #include +#include +#include #define XLIB_ILLEGAL_ACCESS #include @@ -28,8 +31,9 @@ typedef struct { Window window; GC graphics_ctx; Pixmap pixel_map; - XImage* image; - int width, height; + XImage** images; + pthread_t update_thread; + pthread_mutex_t recreate_lock; } WindowState; static void init_x() { @@ -89,43 +93,144 @@ static XImage* create_image(int width, int height, void* data) { ); } +static Swapchain create_swapchain(int width, int height, int frames, int bit_depth, WindowState* state) { + Swapchain swapchain; + swapchain.width = width; + swapchain.height = height; + swapchain.image_count = frames; + swapchain.image_front = 0; + swapchain.image_recent = 0; + swapchain.image_current = 0; + swapchain.images = malloc(sizeof(uint32_t*) * frames); + + state->images = malloc(sizeof(XImage*) * frames); + state->pixel_map = XCreatePixmap(dpy, state->window, width, height, bit_depth); + state->graphics_ctx = XCreateGC(dpy, state->pixel_map, 0, NULL); + + size_t pixel_count = width * height; + for (int i = 0; i < frames; i++) { + uint32_t* data = malloc(pixel_count * bit_depth / 8); + memset(data, 0, pixel_count * bit_depth / 8); + + swapchain.images[i] = data; + state->images[i] = create_image(width, height, data); + + } + + pthread_mutex_init(&state->recreate_lock, NULL); + pthread_mutex_init(&swapchain.lock, NULL); + + return swapchain; +} + +static void free_swapchain(Swapchain swapchain, WindowState* state) { + for (uint8_t i = 0; i < swapchain.image_count; i++) { + XDestroyImage(state->images[i]); // also frees pixel buffer :3 + } + + free(swapchain.images); + free(state->images); + + XFreePixmap(dpy, state->pixel_map); + XFreeGC(dpy, state->graphics_ctx); +} + +static void recreate_swapchain(int width, int height, int bit_depth, Swapchain* swapchain, WindowState* state) { + pthread_mutex_lock(&state->recreate_lock); + + swapchain->width = width; + swapchain->height = height; + + XFreePixmap(dpy, state->pixel_map); + XFreeGC(dpy, state->graphics_ctx); + + state->pixel_map = XCreatePixmap(dpy, state->window, width, height, bit_depth); + state->graphics_ctx = XCreateGC(dpy, state->pixel_map, 0, NULL); + + size_t pixel_count = width * height; + for (uint8_t i = 0; i < swapchain->image_count; i++) { + XDestroyImage(state->images[i]); // also frees pixel buffer :3 + + uint32_t* data = malloc(pixel_count * bit_depth / 8); + memset(data, 0, pixel_count * bit_depth / 8); + + swapchain->images[i] = data; + state->images[i] = create_image(width, height, data); + } + + pthread_mutex_unlock(&state->recreate_lock); +} + +void swapchain_next(Swapchain* swapchain) { + pthread_mutex_lock(&swapchain->lock); + uint8_t next = swapchain->image_current + 1; + next %= swapchain->image_count; + + if (next == swapchain->image_front) { + next += 1; + next %= 3; + } + + swapchain->image_current = next; + pthread_mutex_unlock(&swapchain->lock); +} + +void swapchain_submit(Swapchain* swapchain) { + pthread_mutex_lock(&swapchain->lock); + swapchain->image_recent = swapchain->image_current; + pthread_mutex_unlock(&swapchain->lock); +} + +static void* swapchain_thread(void* arg) { + struct Screen* screen = (struct Screen*) arg; + WindowState* state = (WindowState*) screen->internal; + + while (1) { + pthread_mutex_lock(&state->recreate_lock); + + pthread_mutex_lock(&screen->swapchain.lock); + int index = screen->swapchain.image_recent; + screen->swapchain.image_front = index; + pthread_mutex_unlock(&screen->swapchain.lock); + + XPutImage( + dpy, + state->window, + state->graphics_ctx, + state->images[index], + 0, 0, 0, 0, + screen->swapchain.width, + screen->swapchain.height + ); + XSync(dpy, 0); + + pthread_mutex_unlock(&state->recreate_lock); + + usleep(1000 * 1000 / 60); + } + + return NULL; +} + +#define FRAMES 3 void init_screen(struct Screen* screen, uint16_t width, uint16_t height, const char* title) { if (dpy == NULL) { init_x(); } - - screen->width = width; - screen->height = height; - screen->delta = 0; - size_t pixel_count = screen->width * screen->height; - - screen->pixels = malloc(pixel_count * bit_depth / 8); - memset(screen->pixels, 0, pixel_count * bit_depth / 8); - Window window = create_window(0, 0, width, height, bit_depth, title); XMapWindow(dpy, window); - Pixmap pixel_map = XCreatePixmap(dpy, window, width, height, bit_depth); - - GC gc = XCreateGC(dpy, pixel_map, 0, NULL); - WindowState* state = malloc(sizeof(WindowState)); state->window = window; - state->graphics_ctx = gc; - state->pixel_map = pixel_map; - XWindowAttributes xwa; - XGetWindowAttributes(dpy, window, &xwa); - state->width = xwa.width; - state->height = xwa.height; + screen->swapchain = create_swapchain(width, height, FRAMES, bit_depth, state); + pthread_create(&state->update_thread, NULL, &swapchain_thread, screen); + + screen->delta = 0; screen->internal = state; - - XImage* image = create_image(state->width, state->height, screen->pixels); - state->image = image; - count++; } @@ -147,27 +252,10 @@ static void update_delta(struct Screen* screen) { last = now; } -static void draw_screen(struct Screen* screen) { - WindowState* state = (WindowState*) screen->internal; - - XPutImage( - dpy, - state->window, - state->graphics_ctx, - state->image, - 0, 0, 0, 0, - state->width, - state->height - ); -} - - -static void handle_event(WindowState* state) { +static void handle_event(struct Screen* screen) { XEvent event; XNextEvent(dpy, &event); - switch(event.type) { - case KeyPress: { XKeyEvent xkpe = event.xkey; uint32_t code = xkpe.keycode - dpy->min_keycode; @@ -185,9 +273,8 @@ static void handle_event(WindowState* state) { case ConfigureNotify: { XConfigureEvent xce = event.xconfigure; - state->width = xce.width; - state->height = xce.height; - + if (xce.width != screen->swapchain.width || xce.height != screen->swapchain.height) + recreate_swapchain(xce.width, xce.height, bit_depth, &screen->swapchain, (WindowState*) screen->internal); break; } @@ -196,13 +283,9 @@ static void handle_event(WindowState* state) { } bool poll_screen(struct Screen* screen) { - - draw_screen(screen); - WindowState* state = (WindowState*) screen->internal; - while (XPending(dpy)) - handle_event(state); + handle_event(screen); update_delta(screen); return true; @@ -211,10 +294,9 @@ bool poll_screen(struct Screen* screen) { void free_screen(struct Screen* screen) { WindowState* state = (WindowState*) screen->internal; + pthread_exit(&state->update_thread); XUnmapWindow(dpy, state->window); - XFreePixmap(dpy, state->pixel_map); - XDestroyImage(state->image); - XFreeGC(dpy, state->graphics_ctx); + free_swapchain(screen->swapchain, state); XDestroyWindow(dpy, state->window); free(screen->internal); diff --git a/src/screen.h b/src/screen.h index e1d80d1..423af7e 100644 --- a/src/screen.h +++ b/src/screen.h @@ -1,13 +1,28 @@ #pragma once -#define _POSIX_C_SOURCE 200809L +#define _XOPEN_SOURCE 600 +#define _POSIX_C_SOURCE 200112L +// #define _POSIX_C_SOURCE 200809L #include #include +#include -struct Screen { +typedef struct { uint16_t width; uint16_t height; - uint32_t* pixels; + uint8_t image_count; // amount of frames in flight + uint8_t image_front; // image being drawn to screen + uint8_t image_recent; // last image that finished drawing + uint8_t image_current; // current image being drawn + pthread_mutex_t lock; + uint32_t** images; +} Swapchain; + +void swapchain_next(Swapchain* swapchain); +void swapchain_submit(Swapchain* swapchain); + +struct Screen { + Swapchain swapchain; float delta; void* internal; };