diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-02-07 20:39:58 +0100 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-02-07 20:39:58 +0100 |
| commit | dae5bc02b1c934075e95694953b4330676e21611 (patch) | |
| tree | faa1a80849e5642d0b4bd8b4a91331b1da5b75bf /src | |
| parent | fef523a8d7c87f272de18c8abd57e0cc53e2ef40 (diff) | |
estd: add graphics module
Diffstat (limited to 'src')
| -rw-r--r-- | src/estd/graphics/root.zig | 197 | ||||
| -rw-r--r-- | src/estd/root.zig | 1 | ||||
| -rw-r--r-- | src/screen/drm/buffer/raw.zig | 8 | ||||
| -rw-r--r-- | src/screen/drm/buffer/root.zig | 18 | ||||
| -rw-r--r-- | src/screen/drm/card.zig | 20 | ||||
| -rw-r--r-- | src/screen/drm/connector/root.zig | 8 | ||||
| -rw-r--r-- | src/screen/drm/crtc.zig | 5 | ||||
| -rw-r--r-- | src/screen/drm/encoder.zig | 6 | ||||
| -rw-r--r-- | src/screen/drm/resources.zig | 19 | ||||
| -rw-r--r-- | src/screen/main.zig | 88 |
10 files changed, 280 insertions, 90 deletions
diff --git a/src/estd/graphics/root.zig b/src/estd/graphics/root.zig new file mode 100644 index 0000000..da09450 --- /dev/null +++ b/src/estd/graphics/root.zig @@ -0,0 +1,197 @@ +const std = @import("std"); + +pub const Pixel = packed struct(u32) { + blue: u8, + green: u8, + red: u8, + _padding: u8 = 0x0, +}; + +pub const Canvas = struct { + const Self = @This(); + + width: u32, + height: u32, + buffer: []volatile Pixel, + + pub fn fill(self: *const Canvas, pixel: Pixel) void { + @memset(self.buffer, pixel); + } + + fn gaussian_mean(values: [9]u32) u32 { + return (values[0] + values[1] * 2 + values[2] + + values[3] * 2 + values[4] * 8 + values[5] * 2 + + values[6] + values[7] * 2 + values[8]) / 20; + } + + pub fn set_with_anti_aliasing(self: *const Canvas, x: u32, y: u32) void { + var neighbors_red: [9]u32 = undefined; + var neighbors_green: [9]u32 = undefined; + var neighbors_blue: [9]u32 = undefined; + + for (0..3) |h| { + for (0..3) |w| { + const pixel = self.buffer[(y + 1 - h) * self.width + x + 1 - w]; + neighbors_red[w + 3 * h] = pixel.red; + neighbors_green[w + 3 * h] = pixel.green; + neighbors_blue[w + 3 * h] = pixel.blue; + } + } + + self.buffer[x + (y * self.width)] = .{ + .red = @intCast(Self.gaussian_mean(neighbors_red)), + .green = @intCast(Self.gaussian_mean(neighbors_green)), + .blue = @intCast(Self.gaussian_mean(neighbors_blue)), + }; + } +}; + +pub const Box = struct { + const Self = @This(); + + x: u32, + y: u32, + width: u32, + height: u32, + color: Pixel, + radius: u32 = 0, + + fn render_rect( + self: *const Self, + x: u32, + y: u32, + width: u32, + height: u32, + canvas: *const Canvas + ) void { + if (x >= canvas.width or y >= canvas.height) { + return; + } + + const x_end = @min(x + width, canvas.width); + const y_end = @min(y + height, canvas.height); + + for (y..y_end) |yp| { + const offset = yp * canvas.width; + @memset( + canvas.buffer[(offset + x)..(offset + x_end)], + self.color + ); + } + } + + pub fn render(self: *const Self, canvas: *const Canvas) void { + if (self.radius == 0) { + self.render_rect(self.x, self.y, self.width, self.height, canvas); + return; + } + + const radius = @min(self.radius, self.width / 2, self.height / 2); + const core_width = self.width - 2 * radius; + const core_height = self.height - 2 * radius; + + self.render_rect( + self.x + radius, + self.y, + core_width, + radius, + canvas + ); + + self.render_rect( + self.x + radius, + self.y + radius + core_height, + core_width, + radius, + canvas + ); + + self.render_rect( + self.x, + self.y + radius, + self.width, + core_height, + canvas + ); + + for (0..radius) |yp| { + const width: u32 = @intFromFloat(std.math.round( + @as(f64, @floatFromInt(radius)) * + @cos(std.math.asin( + @as(f64, @floatFromInt(yp)) / @as(f64, @floatFromInt(radius)) + )) + )); + const spacing = radius - width; + + var offset = ((radius - yp) + self.y) * canvas.width + self.x + spacing; + @memset( + canvas.buffer[offset..offset + width], + self.color + ); + + offset = ((radius - yp) + self.y) * canvas.width + self.x + core_width + radius; + @memset( + canvas.buffer[offset..offset + width], + self.color + ); + + offset = (yp + self.y + radius + core_height) * canvas.width + self.x + spacing; + @memset( + canvas.buffer[offset..offset + width], + self.color + ); + + offset = (yp + self.y + radius + core_height) * canvas.width + self.x + core_width + radius; + @memset( + canvas.buffer[offset..offset + width], + self.color + ); + } + + for (0..radius) |yp| { + const width: u32 = @intFromFloat(std.math.round( + @as(f64, @floatFromInt(radius)) * + @cos(std.math.asin( + @as(f64, @floatFromInt(yp)) / @as(f64, @floatFromInt(radius)) + )) + )); + + const spacing = radius - width; + + canvas.set_with_anti_aliasing( + self.x + spacing, + @intCast(self.y + radius - yp), + ); + + canvas.set_with_anti_aliasing( + self.x + spacing - 1, + @intCast(self.y + radius - yp), + ); + + canvas.set_with_anti_aliasing( + self.x + core_width + radius + width, + @intCast(self.y + radius - yp), + ); + + canvas.set_with_anti_aliasing( + self.x + spacing, + @intCast(self.y + yp + radius + core_height), + ); + + canvas.set_with_anti_aliasing( + self.x + core_width + radius + width, + @intCast(self.y + yp + radius + core_height), + ); + } + + for (0..core_width) |w| { + canvas.set_with_anti_aliasing(@intCast(self.x + radius + w), self.y); + canvas.set_with_anti_aliasing(@intCast(self.x + radius + w), self.y + self.height); + } + + for (0..core_height) |h| { + canvas.set_with_anti_aliasing(self.x, @intCast(self.y + radius + h)); + canvas.set_with_anti_aliasing(self.x + self.width, @intCast(self.y + radius + h)); + } + } +}; diff --git a/src/estd/root.zig b/src/estd/root.zig index 00c2631..5ff292e 100644 --- a/src/estd/root.zig +++ b/src/estd/root.zig @@ -2,6 +2,7 @@ const std = @import("std"); pub const cursor = @import("cursor.zig"); pub const parser = @import("parser/root.zig"); +pub const graphics = @import("graphics/root.zig"); test { std.testing.refAllDecls(@This()); diff --git a/src/screen/drm/buffer/raw.zig b/src/screen/drm/buffer/raw.zig index a79c6a7..c5a04f3 100644 --- a/src/screen/drm/buffer/raw.zig +++ b/src/screen/drm/buffer/raw.zig @@ -6,16 +6,16 @@ const cerror = @import("../../cerror.zig"); pub const Raw = struct { const Self = @This(); - card: *drm.Card, + card: drm.Card, + id: u32, width: u32, height: u32, stride: u32, handle: u32, - pixels: []volatile drm.Pixel, bytes: []volatile u8, - pub fn init(card: *drm.Card, width: u32, height: u32) !Self { + pub fn init(card: drm.Card, width: u32, height: u32) !Self { var create_dumb = std.mem.zeroInit(drm.request.CreateDumb, .{ .width = width, .height = height, @@ -32,7 +32,6 @@ pub const Raw = struct { .height = create_dumb.height, .stride = create_dumb.pitch, .handle = create_dumb.handle, - .pixels = undefined, .bytes = undefined, .card = card, }; @@ -74,7 +73,6 @@ pub const Raw = struct { try cerror.from_usize(address); - fb.pixels = @as([*]volatile drm.Pixel, @ptrFromInt(address))[0..create_dumb.size / 4]; fb.bytes = @as([*]volatile u8, @ptrFromInt(address))[0..create_dumb.size]; return fb; diff --git a/src/screen/drm/buffer/root.zig b/src/screen/drm/buffer/root.zig index d40fd8d..f14ed66 100644 --- a/src/screen/drm/buffer/root.zig +++ b/src/screen/drm/buffer/root.zig @@ -1,4 +1,5 @@ const drm = @import("../root.zig"); +const graphics = @import("estd").graphics; pub const Buffer = struct { const Self = @This(); @@ -7,7 +8,7 @@ pub const Buffer = struct { current: Raw, crtc: Raw, - pub fn init(card: *drm.Card, width: u32, height: u32) !Self { + pub fn init(card: drm.Card, width: u32, height: u32) !Self { return .{ .current = try Raw.init(card, width, height), .crtc = try Raw.init(card, width, height), @@ -15,10 +16,6 @@ pub const Buffer = struct { } pub fn swap(self: *Self) void { - const pixels = self.current.pixels; - self.current.pixels = self.crtc.pixels; - self.crtc.pixels = pixels; - const bytes = self.current.bytes; self.current.bytes = self.crtc.bytes; self.crtc.bytes = bytes; @@ -32,5 +29,16 @@ pub const Buffer = struct { self.current.deinit(); self.crtc.deinit(); } + + pub fn canvas(self: *Self) graphics.Canvas { + return .{ + .width = self.current.width, + .height = self.current.height, + .buffer = @as( + [*]volatile graphics.Pixel, + @ptrCast(@alignCast(self.current.bytes)) + )[0..self.current.bytes.len / 4], + }; + } }; diff --git a/src/screen/drm/card.zig b/src/screen/drm/card.zig index ef56142..b5ca893 100644 --- a/src/screen/drm/card.zig +++ b/src/screen/drm/card.zig @@ -25,32 +25,20 @@ pub const Card = struct { }; } - pub fn close(self: *Card) void { + pub fn close(self: Card) void { self.file.close(); } - pub fn resources(self: *Card) !drm.Resources { + pub fn resources(self: Card) !drm.Resources { return drm.Resources.init(self); } - pub fn connector(self: *Card, id: u32) !drm.Connector { - return drm.Connector.init(self, id); - } - - pub fn encoder(self: *Card, id: u32) !drm.Encoder { - return drm.Encoder.init(self, id); - } - - pub fn crtc(self: *Card, id: u32) !drm.Crtc { - return drm.Crtc.init(self, id); - } - - pub fn create_buffer(self: *Card, width: u32, height: u32) !drm.Buffer { + pub fn create_buffer(self: Card, width: u32, height: u32) !drm.Buffer { return drm.Buffer.init(self, width, height); } pub fn request( - self: *Self, + self: *const Self, comptime kind: drm.request.Kind, payload: anytype ) !void { diff --git a/src/screen/drm/connector/root.zig b/src/screen/drm/connector/root.zig index 64686b9..3628817 100644 --- a/src/screen/drm/connector/root.zig +++ b/src/screen/drm/connector/root.zig @@ -9,7 +9,7 @@ pub const Connector = struct { const Self = @This(); - card: *drm.Card, + card: drm.Card, encoder_ids: []u32, modes: []Mode, @@ -25,7 +25,7 @@ pub const Connector = struct { subpixel: u32, pad: u32, - pub fn raw_without_ids(card: *drm.Card, id: u32) !drm.request.Connector { + pub fn raw_without_ids(card: drm.Card, id: u32) !drm.request.Connector { var result = std.mem.zeroInit(drm.request.Connector, .{ .id = id }); try card.request(.get_connector, &result); return result; @@ -34,7 +34,7 @@ pub const Connector = struct { // NOTE: This function does not take in account // that there might be some hot-plugging going // on. This might have to change in the future. - pub fn init(card: *drm.Card, id: u32) !Self { + pub fn init(card: drm.Card, id: u32) !Self { var raw = try Self.raw_without_ids(card, id); const resources = .{ .encoder_ids = try card.allocator.alloc(u32, raw.count_encoders), @@ -76,7 +76,7 @@ pub const Connector = struct { pub fn compatible_crtc(self: *Self) ?usize { for (self.encoder_ids) |encoder_id| { - var encoder = self.card.encoder(encoder_id) catch continue; + var encoder = drm.Encoder.init(self.card, encoder_id) catch continue; defer encoder.deinit(); for (0..32) |index| { diff --git a/src/screen/drm/crtc.zig b/src/screen/drm/crtc.zig index 6130c86..6dc6c3b 100644 --- a/src/screen/drm/crtc.zig +++ b/src/screen/drm/crtc.zig @@ -4,6 +4,8 @@ const drm = @import("root.zig"); pub const Crtc = struct { const Self = @This(); + card: drm.Card, + connector_ids: ?*u32, count_connectors: u32, id: u32, @@ -13,9 +15,8 @@ pub const Crtc = struct { mode: drm.Connector.Mode, mode_valid: u32, gamma_size: u32, - card: *drm.Card, - pub fn init(card: *drm.Card, id: u32) !Self { + pub fn init(card: drm.Card, id: u32) !Self { var raw = std.mem.zeroInit(drm.request.Crtc, .{ .id = id }); try card.request(.get_crtc, &raw); return .{ diff --git a/src/screen/drm/encoder.zig b/src/screen/drm/encoder.zig index 19630ab..2758ee8 100644 --- a/src/screen/drm/encoder.zig +++ b/src/screen/drm/encoder.zig @@ -1,6 +1,5 @@ const std = @import("std"); const drm = @import("root.zig"); -const Card = drm.Card; pub const Encoder = struct { const Self = @This(); @@ -17,13 +16,14 @@ pub const Encoder = struct { dpi = 8, }; + card: drm.Card, + id: u32, type: Kind, possible_crtcs: u32, possible_clones: u32, - card: *Card, - pub fn init(card: *Card, id: u32) !Self { + pub fn init(card: drm.Card, id: u32) !Self { var raw = std.mem.zeroInit(drm.request.Encoder, .{ .id = id }); try card.request(.get_encoder, &raw); diff --git a/src/screen/drm/resources.zig b/src/screen/drm/resources.zig index 9a8566c..8b45fdf 100644 --- a/src/screen/drm/resources.zig +++ b/src/screen/drm/resources.zig @@ -3,13 +3,12 @@ const os = std.os.linux; const cerror = @import("../cerror.zig"); const drm = @import("root.zig"); -const Card = drm.Card; pub const Resources = struct { const Self = @This(); const Range = struct { min: u32, max: u32 }; - card: *Card, + card: drm.Card, fb_ids: []u32, crtc_ids: []u32, @@ -18,7 +17,7 @@ pub const Resources = struct { width: Range, height: Range, - pub fn raw_without_ids(card: *Card) !drm.request.Resources { + pub fn raw_without_ids(card: drm.Card) !drm.request.Resources { var result = std.mem.zeroes(drm.request.Resources); try card.request(.get_resources, &result); return result; @@ -27,7 +26,7 @@ pub const Resources = struct { // NOTE: This function does not take in account // that there might be some hot-plugging going // on. This might have to change in the future. - pub fn init(card: *Card) !Self { + pub fn init(card: drm.Card) !Self { var raw = try Self.raw_without_ids(card); const resources = .{ .fb_ids = try card.allocator.alloc(u32, raw.count_fbs), @@ -59,4 +58,16 @@ pub const Resources = struct { self.card.allocator.free(self.connector_ids); self.card.allocator.free(self.encoder_ids); } + + pub fn connector(self: *Self, index: usize) !drm.Connector { + return drm.Connector.init(self.card, self.connector_ids[index]); + } + + pub fn crtc(self: *Self, index: usize) !drm.Crtc { + return drm.Crtc.init(self.card, self.crtc_ids[index]); + } + + pub fn encoder(self: *Self, index: usize) !drm.Encoder { + return drm.Encoder.init(self.card, self.encoder_ids[index]); + } }; diff --git a/src/screen/main.zig b/src/screen/main.zig index 0514210..7b73450 100644 --- a/src/screen/main.zig +++ b/src/screen/main.zig @@ -1,5 +1,6 @@ const std = @import("std"); const drm = @import("drm/root.zig"); +const graphics = @import("estd").graphics; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; @@ -11,14 +12,15 @@ pub fn main() !void { var resources = try card.resources(); defer resources.deinit(); - var connector = try card.connector(resources.connector_ids[0]); + var connector = try resources.connector(0); defer connector.deinit(); - const mode = connector.modes[0]; + const mode = connector.modes[12]; + std.debug.print("using mode: {s}@{d:.2}\n", .{mode.name, mode.frame_rate()}); - var crtc = try card.crtc(resources.crtc_ids[ + var crtc = try resources.crtc( connector.compatible_crtc() orelse @panic("no crtc found") - ]); + ); defer crtc.detach(); var buffer = try card.create_buffer(mode.horizontal.size, mode.vertical.size); @@ -26,62 +28,46 @@ pub fn main() !void { try crtc.attach(&buffer, &connector, mode); - const Pos = struct { x: f32, y: f32 }; + const center_x: i64 = @intCast(buffer.current.width / 2 - 100); + const center_y: i64 = @intCast(buffer.current.height / 2 - 100); - var vec = Pos { .x = 10, .y = 10 }; - var pos = Pos { .x = 0, .y = 0 }; - var delta: f32 = 0; - const size = 100; + var box = graphics.Box { + .x = @intCast(center_x), + .y = @intCast(center_y), + .width = 200, + .height = 200, + .color = .{ .red = 0xff, .green = 0x05, .blue = 0x07 }, + .radius = 0, + }; - const width: f32 = @floatFromInt(buffer.current.width); - const height: f32 = @floatFromInt(buffer.current.height); + var last_duration: f64 = 0; + var dir: f64 = 1; + var delta: f64 = 0; + var radius: f64 = 0; while (true) { const start = try std.time.Instant.now(); + const canvas = buffer.canvas(); + canvas.fill(.{ .red = 0x25, .green = 0x25, .blue = 0x25 }); + box.render(&canvas); - @memset( - buffer.current.pixels, - .{ - .red = 0x25, - .green = 0x25, - .blue = 0x25 - } - ); - - pos.x += vec.x * delta; - pos.y += vec.y * delta; - - if (pos.x < 0) { - pos.x = -pos.x; - vec.x = -vec.x; - } else if (pos.x + size >= width) { - pos.x = width - size; - vec.x = -vec.x; - } - - if (pos.y < 0) { - pos.y = -pos.y; - vec.y = -vec.y; - } else if (pos.y + size >= height) { - pos.y = height - size; - vec.y = -vec.y; - } - - for (0..size) |w| { - for (0..size) |h| { - const x = @as(usize, @intFromFloat(pos.x)) + w; - const y = @as(usize, @intFromFloat(pos.y)) + h; - buffer.current.pixels[x + buffer.current.width * y] = .{ - .red = 0xff, - .green = 0, - .blue = 0, - }; - } + radius += delta * dir; + if (radius > 100) { + std.debug.print("HERE\n", .{}); + radius = 100; + dir = -1; + } else if (radius < 0) { + radius = 0; + dir = 1; } + box.radius = @intFromFloat(radius); const end = try std.time.Instant.now(); - const elapsed = (@as(f32, @floatFromInt(end.since(start))) / std.time.ns_per_s); - delta = elapsed * 60; + const duration: f64 = @floatFromInt(end.since(start)); + const average = (duration + last_duration) / 2; + last_duration = duration; + std.debug.print("@FPS: {d:.3}\r", .{ std.time.ns_per_s / average }); + delta = 60 * last_duration / std.time.ns_per_s; try crtc.page_flip(&buffer); |