~kennylevinsen/wlavu

1010fc4adb63b5988a859c288191e50d73b75ffa — Kenny Levinsen 3 years ago 2d70133
Switch from EGL to software rendering
2 files changed, 218 insertions(+), 134 deletions(-)

M main.c
M meson.build
M main.c => main.c +216 -129
@@ 1,20 1,20 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <sys/poll.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <GL/glut.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
#include <math.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
#include <wayland-egl.h>

#ifdef __linux__
#include <linux/input-event-codes.h>
#elif __FreeBSD__


@@ 40,8 40,130 @@ enum event_loop_fd {
	EVENT_LOOP_PIPEWIRE,
};

static void randname(char *buf) {
        struct timespec ts;
        clock_gettime(CLOCK_REALTIME, &ts);
        long r = ts.tv_nsec;
        for (int i = 0; i < 6; ++i) {
                buf[i] = 'A'+(r&15)+(r&16)*2;
                r >>= 5;
        }
}

static int anonymous_shm_open(void) {
        char name[] = "/wlavu-XXXXXX";
        int retries = 100;

        do {
                randname(name + strlen(name) - 6);
                --retries;
                // shm_open guarantees that O_CLOEXEC is set
                int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
                if (fd >= 0) {
                        shm_unlink(name);
                        return fd;
                }
        } while (retries > 0 && errno == EEXIST);

        return -1;
}

static int create_shm_file(off_t size) {
        int fd = anonymous_shm_open();
        if (fd < 0) {
                return fd;
        }

        if (ftruncate(fd, size) < 0) {
                close(fd);
                return -1;
        }

        return fd;
}

struct shm_buf {
	uint32_t *data;
	int width;
	int height;
	int size;
	bool inuse;

	struct wl_buffer *buffer;
};

static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer) {
	struct shm_buf *buf = data;
	buf->inuse = false;
}

static const struct wl_buffer_listener wl_buffer_listener = {
	.release = wl_buffer_release,
};

static int prepare_buffer(struct shm_buf *buf, struct wl_shm *shm, int width, int height) {
	if (buf == NULL) {
		return -1;
	}
	if (buf->data != NULL && buf->width == width && buf->height == height) {
		return 0;
	}

	buf->width = width;
	buf->height = height;

	int stride = width * 4;
	int size = stride * height;
	if (buf->data != NULL) {
		munmap(buf->data, buf->size);
		buf->data = NULL;
	}
	buf->size = size;

	int fd = create_shm_file(size);
	if (fd == -1) {
		return -1;
	}
	buf->data = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
	if (buf->data == MAP_FAILED) {
		close(fd);
		return -1;
	}

	struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
	buf->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888);
	wl_shm_pool_destroy(pool);
	close(fd);

	wl_buffer_add_listener(buf->buffer, &wl_buffer_listener, buf);
	return 0;
}

static void use_buf(struct shm_buf *buf) {
	buf->inuse = true;
}

struct shm_bufs {
	struct shm_buf bufs[3];
	int next;
};

static struct shm_buf *next_buf(struct shm_bufs *bufs) {
	if (!bufs->bufs[0].inuse) {
		return &bufs->bufs[0];
	} else if (!bufs->bufs[1].inuse) {
		return &bufs->bufs[1];
	} else if (!bufs->bufs[2].inuse) {
		return &bufs->bufs[2];
	} else {
		fprintf(stderr, "all buffers in use\n");
		return NULL;
	}
}

struct context {
	struct wl_display *display;
	struct wl_shm *shm;
	struct wl_compositor *compositor;
	struct wl_surface *surface;
	struct xdg_wm_base *xdg_wm_base;


@@ 57,12 179,15 @@ struct context {
	struct pw_loop *pw_loop;
	struct pw_stream *pw_stream;

	struct shm_bufs bufs;

	int width;
	int height;

	bool dirty;
	bool pending;
	bool running;
	int last_extend;

	int channels;
	int rate;


@@ 74,12 199,6 @@ struct context {
	double last_val;
	double last_peak;
	double peak_decay;

	struct wl_egl_window *wl_egl_window;
	EGLConfig egl_config;
	EGLContext egl_context;
	EGLDisplay egl_display;
	EGLSurface egl_surface;
};

static void layer_surface_configure(void *data,


@@ 88,13 207,6 @@ static void layer_surface_configure(void *data,
	struct context *ctx = data;
	ctx->width = width;
	ctx->height = height;
	if (ctx->wl_egl_window) {
		wl_egl_window_resize(ctx->wl_egl_window, width, height, 0, 0);
	}
	if (ctx->egl_surface) {
		eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context);
		glViewport(0, 0, width, height);
	}
	zwlr_layer_surface_v1_ack_configure(surface, serial);
}



@@ 111,6 223,7 @@ struct zwlr_layer_surface_v1_listener layer_surface_listener = {
	.closed = layer_surface_closed,
};

static int draw(struct context *ctx);
static void xdg_toplevel_configure(void *data,
		struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height,
		struct wl_array *states) {


@@ 123,13 236,8 @@ static void xdg_toplevel_configure(void *data,
	}
	ctx->width = width;
	ctx->height = height;
	if (ctx->wl_egl_window) {
		wl_egl_window_resize(ctx->wl_egl_window, width, height, 0, 0);
	}
	if (ctx->egl_surface != NULL) {
		eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context);
		glViewport(0, 0, width, height);
	}
	ctx->dirty = true;
	draw(ctx);
}

static void xdg_surface_handle_configure(void *data,


@@ 200,7 308,9 @@ static void handle_global(void *data, struct wl_registry *registry,
		wl_seat_add_listener(ctx->seat, &seat_listener, ctx);
	} else if (strcmp(interface, wl_compositor_interface.name) == 0) {
		ctx->compositor =
			wl_registry_bind(registry, name, &wl_compositor_interface, 1);
			wl_registry_bind(registry, name, &wl_compositor_interface, 4);
	} else if (strcmp(interface, wl_shm_interface.name) == 0) {
		ctx->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
	} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
		ctx->xdg_wm_base =
			wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);


@@ 221,20 331,6 @@ static const struct wl_registry_listener registry_listener = {
	.global_remove = handle_global_remove,
};

static void draw_horiz(double val) {
	glVertex2f(-1,-1);
	glVertex2f(2*val-1, -1);
	glVertex2f(2*val-1, 1);
	glVertex2f(-1, 1);
}

static void draw_vert(double val) {
	glVertex2f(-1,-1);
	glVertex2f(1, -1);
	glVertex2f(1, 2*val-1);
	glVertex2f(-1, 2*val-1);
}

static int draw(struct context *ctx);
static void frame_handle_done(void *data, struct wl_callback *callback,
		uint32_t time) {


@@ 249,6 345,63 @@ const struct wl_callback_listener frame_listener = {
	.done = frame_handle_done,
};

static int max(int a, int b) {
	return a > b ? a : b;
}

static int draw_vert(struct context *ctx, struct shm_buf *buf) {
	int peak_rows = (ctx->peak * (double)buf->height);
	int val_rows = (ctx->val * (double)buf->height);

	int peak_start = (buf->height - peak_rows) * buf->width;
	int val_start = (buf->height - val_rows) * buf->width;

	int black_color = 0xFF000000;
	int val_color = 0xFF00FF00;
	int peak_color = 0xFF404040;
	if (ctx->peak > 0.9) {
		peak_color = 0xFF800000;
	}

	int idx = 0;
	while (idx < peak_start) {
		buf->data[idx++] = black_color;
	}
	while (idx < val_start) {
		buf->data[idx++] = peak_color;
	}
	while (idx < buf->height * buf->width) {
		buf->data[idx++] = val_color;
	}
	return peak_rows;
}

static int draw_horiz(struct context *ctx, struct shm_buf *buf) {
	int peak_cols = (ctx->peak * (double)buf->width);
	int val_cols = (ctx->val * (double)buf->width);

	int black_color = 0xFF000000;
	int val_color = 0xFF00FF00;
	int peak_color = 0xFF404040;
	if (ctx->peak > 0.9) {
		peak_color = 0xFF800000;
	}
	for (int row = 0; row < buf->height; row++) {
		int offset = row * buf->width;
		int idx = offset;
		while (idx < offset + val_cols) {
			buf->data[idx++] = val_color;
		}
		while (idx < offset + peak_cols) {
			buf->data[idx++] = peak_color;
		}
		while (idx < offset + buf->width) {
			buf->data[idx++] = black_color;
		}
	}
	return peak_cols;
}

static int draw(struct context *ctx) {
	if (ctx->pending) {
		return 0;


@@ 258,41 411,33 @@ static int draw(struct context *ctx) {
	}
	ctx->dirty = false;

	glClear(GL_COLOR_BUFFER_BIT);

	void (*draw_bar)(double val);
	if (ctx->height > ctx->width) {
		draw_bar = draw_vert;
	} else {
		draw_bar = draw_horiz;
	struct shm_buf *buf = next_buf(&ctx->bufs);
	if (prepare_buffer(buf, ctx->shm, ctx->width, ctx->height) == -1) {
		return -1;
	}

	if (ctx->peak > 0.9) {
		glColor3f(.5, 0.0, 0.0);
	use_buf(buf);

	struct wl_callback *callback = wl_surface_frame(ctx->surface);
	wl_callback_add_listener(callback, &frame_listener, ctx);
	ctx->pending = true;

	if (buf->height > buf->width) {
		int extend = draw_vert(ctx, buf);
		wl_surface_damage_buffer(ctx->surface, 0, ctx->height - max(ctx->last_extend, extend), buf->width, max(ctx->last_extend, extend));
		ctx->last_extend = extend;
	} else {
		glColor3f(0.25, .25, 0.25);
		int extend = draw_horiz(ctx, buf);
		wl_surface_damage_buffer(ctx->surface, 0, 0, max(ctx->last_extend, extend), buf->height);
		ctx->last_extend = extend;
	}
	glBegin(GL_POLYGON);
	draw_bar(ctx->peak);
	glEnd();

	glColor3f(0.0, 1.0, 0.0);
	glBegin(GL_POLYGON);
	draw_bar(ctx->val);
	glEnd();
	wl_surface_attach(ctx->surface, buf->buffer, 0, 0);
	wl_surface_commit(ctx->surface);

	ctx->pending = true;
	if (ctx->peak_decay != 0) {
		ctx->peak_decay--;
	}
	struct wl_callback *callback = wl_surface_frame(ctx->surface);
	wl_callback_add_listener(callback, &frame_listener, ctx);

	if (!eglSwapBuffers(ctx->egl_display, ctx->egl_surface)) {
		fprintf(stderr, "eglSwapBuffers failed\n");
		return -1;
	}

	return 0;
}



@@ 343,15 488,11 @@ static bool equal_within(double a, double b, double hysteresis) {
}

static double max_f32(void *buffer, int frames) {
	float max = 0;
	double max = 0;
	for (int idx = 0; idx < frames; idx++) {
		float val = fabsf(((float*)buffer)[idx]);
		if (val > max) {
			max = val;
		}
		max = fmax(fabs(((float*)buffer)[idx]), max);
	}

	return max;
	return fmin(max, 1.0);
}

static void on_process(void *userdata) {


@@ 479,47 620,6 @@ static int setup_wayland(struct context *ctx, bool layer_shell, bool vertical, i
		return -1;
	}

	ctx->egl_display = eglGetDisplay((EGLNativeDisplayType)ctx->display);
	if (ctx->egl_display == EGL_NO_DISPLAY) {
		fprintf(stderr, "failed to create EGL display\n");
		return -1;
	}

	eglBindAPI(EGL_OPENGL_API);

	EGLint major, minor;
	if (!eglInitialize(ctx->egl_display, &major, &minor)) {
		fprintf(stderr, "failed to initialize EGL\n");
		return -1;
	}

	EGLint count;
	eglGetConfigs(ctx->egl_display, NULL, 0, &count);

	EGLint config_attribs[] = {
		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
		EGL_RED_SIZE, 8,
		EGL_GREEN_SIZE, 8,
		EGL_BLUE_SIZE, 8,
		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
		EGL_NONE,
	};
	EGLint n = 0;
	EGLConfig *configs = calloc(count, sizeof(EGLConfig));
	eglChooseConfig(ctx->egl_display, config_attribs, configs, count, &n);
	if (n == 0) {
		fprintf(stderr, "failed to choose an EGL config\n");
		return -1;
	}
	ctx->egl_config = configs[0];

	EGLint context_attribs[] = {
		EGL_CONTEXT_CLIENT_VERSION, 2,
		EGL_NONE,
	};
	ctx->egl_context = eglCreateContext(ctx->egl_display, ctx->egl_config,
		EGL_NO_CONTEXT, context_attribs);

	ctx->surface = wl_compositor_create_surface(ctx->compositor);

	if (!layer_shell) {


@@ 552,17 652,6 @@ static int setup_wayland(struct context *ctx, bool layer_shell, bool vertical, i

	wl_surface_commit(ctx->surface);
	wl_display_roundtrip(ctx->display);
	
	ctx->wl_egl_window = wl_egl_window_create(ctx->surface, ctx->width, ctx->height);
	ctx->egl_surface = eglCreateWindowSurface(ctx->egl_display, ctx->egl_config,
		(EGLNativeWindowType)ctx->wl_egl_window, NULL);

	if (!eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context)) {
		fprintf(stderr, "Could not make EGL context current\n");
		return -1;
	}

	eglSwapInterval(ctx->egl_display, 0);

	return 0;
}


@@ 650,8 739,6 @@ int main(int argc, char **argv) {
		return EXIT_FAILURE;
	}

	glutInit(&argc, argv);

	if (setup_wayland(&ctx, use_layer_shell, vertical, anchor) == -1) {
		return EXIT_FAILURE;
	}

M meson.build => meson.build +2 -5
@@ 25,6 25,7 @@ add_project_arguments(
		'-Wl,--exclude-libs=ALL',
		'-D_USE_MATH_DEFINES',
		'-D_XOPEN_SOURCE=700',
		'-ffast-math',
	],
	language: 'c',
)


@@ 55,13 56,9 @@ executable(
	dependencies: [
		protocols_dep,
		cc.find_library('m'),
		cc.find_library('rt'),
		dependency('libpipewire-0.3'),
		dependency('libspa-0.2'),
		cc.find_library('glut'),
		dependency('gl'),
		dependency('glesv2'),
		dependency('egl'),
		dependency('wayland-egl'),
	],
	install: true
)