wlbg: port to OpenGL and fix EGL deadlock/spinning crap

This commit is contained in:
LordMZTE 2024-01-25 19:07:20 +01:00
parent e84433ff11
commit fd75681e2f
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
8 changed files with 101 additions and 27 deletions

View file

@ -37,7 +37,7 @@ pub fn build(b: *std.Build) void {
exe.root_module.linkSystemLibrary("wayland-client", .{});
exe.root_module.linkSystemLibrary("wayland-egl", .{});
exe.root_module.linkSystemLibrary("EGL", .{});
exe.root_module.linkSystemLibrary("GLESv2", .{});
exe.root_module.linkSystemLibrary("GL", .{});
b.installArtifact(exe);
const run_cmd = b.addRunArtifact(exe);

View file

@ -1,5 +1,6 @@
const std = @import("std");
const c = @import("ffi.zig").c;
const wayland = @import("wayland");
const glutil = @import("glutil.zig");
const options = @import("options.zig");
@ -9,12 +10,17 @@ const OutputInfo = @import("OutputInfo.zig");
const OutputWindow = @import("OutputWindow.zig");
const PointerState = @import("PointerState.zig");
const wl = wayland.client.wl;
dpy: *wl.Display,
egl_dpy: c.EGLDisplay,
bg_shader_program: c_uint,
main_shader_program: c_uint,
bg_bufs: std.MultiArrayList(BgBuf),
time: i64,
cursor_positions: [][2]c_int,
vao: c_uint,
vbo: c_uint,
const Gfx = @This();
@ -24,7 +30,7 @@ const BgBuf = struct {
zbuffer: c_uint,
};
pub fn init(egl_dpy: c.EGLDisplay, output_info: []const OutputInfo) !Gfx {
pub fn init(dpy: *wl.Display, egl_dpy: c.EGLDisplay, output_info: []const OutputInfo) !Gfx {
const bg_program = shader: {
const vert_shader = try glutil.createShader(c.GL_VERTEX_SHADER, @embedFile("bg_vert.glsl"));
defer c.glDeleteShader(vert_shader);
@ -125,15 +131,24 @@ pub fn init(egl_dpy: c.EGLDisplay, output_info: []const OutputInfo) !Gfx {
c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0);
c.glBindTexture(c.GL_TEXTURE_2D, 0);
c.glBindRenderbuffer(c.GL_FRAMEBUFFER, 0);
c.glBindRenderbuffer(c.GL_RENDERBUFFER, 0);
var vao: c_uint = 0;
c.glGenVertexArrays(1, &vao);
var vbo: c_uint = 0;
c.glGenBuffers(1, &vbo);
return .{
.dpy = dpy,
.egl_dpy = egl_dpy,
.bg_shader_program = bg_program,
.main_shader_program = main_program,
.bg_bufs = bg_bufs,
.time = 0,
.cursor_positions = cursor_positions,
.vao = vao,
.vbo = vbo,
};
}
@ -197,6 +212,8 @@ pub fn draw(
) !void {
self.time += dt;
dth.should_redraw[output_idx] = false;
c.glBindVertexArray(self.vao);
c.glBindBuffer(c.GL_ARRAY_BUFFER, self.vbo);
c.glBindFramebuffer(c.GL_FRAMEBUFFER, 0); // use default framebuffer
c.glUseProgram(self.main_shader_program);
@ -210,7 +227,8 @@ pub fn draw(
-1.0, 1.0, 0.0, 0.0, 1.0,
};
c.glVertexAttribPointer(0, 3, c.GL_FLOAT, c.GL_FALSE, @sizeOf(f32) * 5, &vertices);
c.glBufferData(c.GL_ARRAY_BUFFER, @sizeOf(f32) * vertices.len, &vertices, c.GL_STATIC_DRAW);
c.glVertexAttribPointer(0, 3, c.GL_FLOAT, c.GL_FALSE, @sizeOf(f32) * 5, null);
c.glEnableVertexAttribArray(0);
c.glVertexAttribPointer(
1,
@ -218,7 +236,7 @@ pub fn draw(
c.GL_FLOAT,
c.GL_FALSE,
@sizeOf(f32) * 5,
@ptrFromInt(@intFromPtr(&vertices) + @sizeOf(f32) * 3),
@ptrFromInt(@sizeOf(f32) * 3),
);
c.glEnableVertexAttribArray(1);
@ -243,8 +261,25 @@ pub fn draw(
c.glDrawArrays(c.GL_TRIANGLES, 0, vertices.len / 3);
if (c.eglSwapInterval(self.egl_dpy, 0) != c.EGL_TRUE or
c.eglSwapBuffers(self.egl_dpy, outputs[output_idx].egl_surface) != c.EGL_TRUE) return error.EGLError;
// This is necessary because some EGL implementations (Mesa) will try reading the wayland
// socket from eglSwapBuffers.
// This causes a deadlock, is abysmal API design, and not preventable. It's gotten so bad
// that another suggested workaround on the archive linked below is to put this on another
// thread in order to force EGL to use its own wayland event queue.
//
// By cancelling the read operation first (which is always prep'd usually and handled by the
// event loop), we can ensure that EGL gets to do its nonsense here before making libwayland
// handle another read correctly by calling prepareRead again.
//
// Somehow the only trace of this BS on the entire internet and only reason I managed to figure
// this out: https://lists.freedesktop.org/archives/wayland-devel/2013-March/007739.html
self.dpy.cancelRead();
if (c.eglSwapBuffers(
self.egl_dpy,
outputs[output_idx].egl_surface,
) != c.EGL_TRUE) return error.EGLError;
if (self.dpy.dispatchPending() != .SUCCESS) return error.RoundtipFail;
std.debug.assert(self.dpy.prepareRead());
}
pub fn drawBackground(
@ -271,6 +306,8 @@ pub fn drawBackground(
-1.0, 1.0, 0.0,
};
c.glBindVertexArray(self.vao);
c.glBindBuffer(c.GL_ARRAY_BUFFER, self.vbo);
c.glBindFramebuffer(c.GL_FRAMEBUFFER, self.bg_bufs.get(output_idx).framebuffer);
c.glClearColor(1.0, 0.0, 0.0, 1.0);
@ -278,7 +315,8 @@ pub fn drawBackground(
c.glUseProgram(self.bg_shader_program);
c.glVertexAttribPointer(0, 3, c.GL_FLOAT, c.GL_FALSE, 0, &vertices);
c.glBufferData(c.GL_ARRAY_BUFFER, @sizeOf(f32) * vertices.len, &vertices, c.GL_STATIC_DRAW);
c.glVertexAttribPointer(0, 3, c.GL_FLOAT, c.GL_FALSE, 0, null);
c.glEnableVertexAttribArray(0);
c.glUniform2f(c.glGetUniformLocation(self.bg_shader_program, "offset"), off.x, off.y);

View file

@ -1,6 +1,4 @@
#version 300 es
precision highp float;
#version 430
uniform float time;
@ -20,7 +18,7 @@ void main() {
vec2 R = vec2(1);
ivec4 b = ivec4(o -= o); // Initialize b=0
float t = .1*time, B, h, z;
vec4 g;
vec4 g = vec4(0);
u =
(5. + cos(t) * 1.5) * // * Camera push in/out

View file

@ -1,6 +1,4 @@
#version 300 es
precision mediump float;
#version 430
uniform vec2 offset;

View file

@ -1,5 +1,8 @@
pub const c = @cImport({
@cInclude("wayland-egl.h"); // required for egl include to work
@cInclude("EGL/egl.h");
@cInclude("GLES2/gl2.h");
@cDefine("GL_GLEXT_PROTOTYPES", "1");
@cInclude("GL/gl.h");
@cInclude("GL/glext.h");
});

View file

@ -42,6 +42,8 @@ pub fn main() !void {
if (dpy.roundtrip() != .SUCCESS) return error.RoundtipFail;
if (c.eglBindAPI(c.EGL_OPENGL_API) == 0) return error.EGLError;
const egl_dpy = c.eglGetDisplay(@ptrCast(dpy)) orelse return error.EGLError;
if (c.eglInitialize(egl_dpy, null, null) != c.EGL_TRUE) return error.EGLError;
defer _ = c.eglTerminate(egl_dpy);
@ -53,7 +55,7 @@ pub fn main() !void {
egl_dpy,
&[_]i32{
c.EGL_SURFACE_TYPE, c.EGL_WINDOW_BIT,
c.EGL_RENDERABLE_TYPE, c.EGL_OPENGL_ES2_BIT,
c.EGL_RENDERABLE_TYPE, c.EGL_OPENGL_BIT,
c.EGL_RED_SIZE, 8,
c.EGL_GREEN_SIZE, 8,
c.EGL_BLUE_SIZE, 8,
@ -72,8 +74,10 @@ pub fn main() !void {
config,
c.EGL_NO_CONTEXT,
&[_]i32{
c.EGL_CONTEXT_MAJOR_VERSION, 2,
c.EGL_CONTEXT_OPENGL_DEBUG, 1,
c.EGL_CONTEXT_MAJOR_VERSION, 4,
c.EGL_CONTEXT_MINOR_VERSION, 3,
c.EGL_CONTEXT_OPENGL_DEBUG, c.EGL_TRUE,
c.EGL_CONTEXT_OPENGL_PROFILE_MASK, c.EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT,
c.EGL_NONE,
},
) orelse return error.EGLError;
@ -188,7 +192,11 @@ pub fn main() !void {
egl_ctx,
) != c.EGL_TRUE) return error.EGLError;
var gfx = try Gfx.init(egl_dpy, output_info);
c.glDebugMessageCallback(&glDebugCb, null);
c.glEnable(c.GL_DEBUG_OUTPUT);
std.log.info("initialized OpenGL {s}", .{c.glGetString(c.GL_VERSION)});
var gfx = try Gfx.init(dpy, egl_dpy, output_info);
defer gfx.deinit();
var rdata = RenderData{
@ -232,6 +240,9 @@ pub fn main() !void {
};
loop.add(&wl_poll_completion);
if (dpy.dispatchPending() != .SUCCESS) return error.RoundtipFail;
std.debug.assert(dpy.prepareRead());
std.log.info("running event loop", .{});
try loop.run(.until_done);
}
@ -349,12 +360,18 @@ fn wlPollCb(
};
const dpy: *wl.Display = @ptrCast(@alignCast(userdata));
if (dpy.dispatchPending() != .SUCCESS or dpy.flush() != .SUCCESS) {
if (dpy.readEvents() != .SUCCESS or
dpy.dispatchPending() != .SUCCESS or
dpy.flush() != .SUCCESS)
{
std.log.err("error processing wayland events", .{});
loop.stop();
return .disarm;
}
// This is only false if the queue is not empty, but we just emptied the queue.
std.debug.assert(dpy.prepareRead());
return .rearm;
}
@ -426,3 +443,27 @@ fn resetXevTimerCompletion(completion: *xev.Completion, now: i64, in: i64) void
.tv_nsec = @mod(next_time, std.time.ms_per_s) * std.time.ns_per_ms,
};
}
fn glDebugCb(
source: c.GLenum,
@"type": c.GLenum,
id: c.GLuint,
severity: c.GLenum,
len: c.GLsizei,
msgp: ?[*:0]const u8,
udata: ?*const anyopaque,
) callconv(.C) void {
_ = source;
_ = @"type";
_ = id;
_ = udata;
const log = std.log.scoped(.gl);
// Mesa likes to include trailing newlines sometimes
const msg = std.mem.trim(u8, msgp.?[0..@intCast(len)], &std.ascii.whitespace);
switch (severity) {
c.GL_DEBUG_SEVERITY_HIGH => log.err("{s}", .{msg}),
c.GL_DEBUG_SEVERITY_MEDIUM, c.GL_DEBUG_SEVERITY_LOW => log.warn("{s}", .{msg}),
c.GL_DEBUG_SEVERITY_NOTIFICATION => log.info("{s}", .{msg}),
else => unreachable,
}
}

View file

@ -1,6 +1,4 @@
#version 300 es
precision highp float;
#version 430
uniform sampler2D bg;

View file

@ -1,6 +1,4 @@
#version 300 es
precision mediump float;
#version 430
in vec4 vPos;
in vec2 uv;