diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-02-02 09:04:45 +0100 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2025-02-02 09:04:45 +0100 |
| commit | 0b6ee849722002a8bc7cc5374e3136bee4be2ccd (patch) | |
| tree | 729e99b7e87d8cd298f3b62cd2b97f362b863216 /src | |
| parent | 88aa2940b27044794d4dcb49c924a5df17cc0055 (diff) | |
screen: add frame-buffer
Diffstat (limited to 'src')
| -rw-r--r-- | src/screen/drm/card.zig | 5 | ||||
| -rw-r--r-- | src/screen/drm/connector/mode.zig | 13 | ||||
| -rw-r--r-- | src/screen/drm/connector/root.zig | 1 | ||||
| -rw-r--r-- | src/screen/drm/crtc/root.zig | 52 | ||||
| -rw-r--r-- | src/screen/drm/frame-buffer/pixelformat.zig | 11 | ||||
| -rw-r--r-- | src/screen/drm/frame-buffer/root.zig | 147 | ||||
| -rw-r--r-- | src/screen/drm/request.zig | 6 | ||||
| -rw-r--r-- | src/screen/main.zig | 62 |
8 files changed, 288 insertions, 9 deletions
diff --git a/src/screen/drm/card.zig b/src/screen/drm/card.zig index 411716a..d66a5f1 100644 --- a/src/screen/drm/card.zig +++ b/src/screen/drm/card.zig @@ -7,6 +7,7 @@ const Resources = @import("resources.zig").Resources; const Connector = @import("connector/root.zig").Connector; const Encoder = @import("encoder/root.zig").Encoder; const Crtc = @import("crtc/root.zig").Crtc; +const FrameBuffer = @import("frame-buffer/root.zig").FrameBuffer; pub const Card = struct { @@ -52,4 +53,8 @@ pub const Card = struct { pub fn crtc(self: *Card, id: u32) !Crtc { return Crtc.init(self, id); } + + pub fn create_frame_buffer(self: *Card, width: u32, height: u32, bpp: u32) !FrameBuffer { + return FrameBuffer.init(self, width, height, bpp); + } }; diff --git a/src/screen/drm/connector/mode.zig b/src/screen/drm/connector/mode.zig index 9f4dfc4..fb61b5b 100644 --- a/src/screen/drm/connector/mode.zig +++ b/src/screen/drm/connector/mode.zig @@ -9,6 +9,17 @@ pub const Mode = extern struct { total: u16, }; + const Type = packed struct(u32) { + builtin: bool, + clock_c: bool, + crtc_c: bool, + preferred: bool, + default: bool, + userdef: bool, + driver: bool, + _padding: u25, + }; + const Flags = packed struct(u32) { phsync: bool, nhsync: bool, @@ -35,7 +46,7 @@ pub const Mode = extern struct { vertical_refresh: u32, flags: Flags, - type: u32, + type: Type, name: [32]u8, pub fn frame_rate(self: *const Self) f32 { diff --git a/src/screen/drm/connector/root.zig b/src/screen/drm/connector/root.zig index 5766851..c104e84 100644 --- a/src/screen/drm/connector/root.zig +++ b/src/screen/drm/connector/root.zig @@ -2,6 +2,7 @@ const std = @import("std"); const os = std.os.linux; const Drm = @import("../request.zig").Drm; const Card = @import("../card.zig").Card; +const FrameBuffer = @import("../frame-buffer/root.zig").FrameBuffer; pub const Connection = @import("connection.zig").Connection; pub const Mode = @import("mode.zig").Mode; diff --git a/src/screen/drm/crtc/root.zig b/src/screen/drm/crtc/root.zig index 7601732..3c9d6f9 100644 --- a/src/screen/drm/crtc/root.zig +++ b/src/screen/drm/crtc/root.zig @@ -2,9 +2,11 @@ const std = @import("std"); const Card = @import("../card.zig").Card; const Drm = @import("../request.zig").Drm; const Connector = @import("../connector/root.zig").Connector; +const FrameBuffer = @import("../frame-buffer/root.zig").FrameBuffer; +const Mode = @import("../connector/mode.zig").Mode; pub const RawCrtc = extern struct { - connectors: ?*Connector, + connector_ids: ?*u32, count_connectors: u32, id: u32, buffer_id: u32, @@ -12,15 +14,19 @@ pub const RawCrtc = extern struct { y: u32, gamma_size: u32, mode_valid: u32, + mode: Mode, }; pub const Crtc = struct { const Self = @This(); + connector_ids: ?*u32, + count_connectors: u32, id: u32, buffer_id: u32, x: u32, y: u32, + mode: Mode, mode_valid: u32, gamma_size: u32, card: *Card, @@ -29,17 +35,57 @@ pub const Crtc = struct { var raw = std.mem.zeroInit(RawCrtc, .{ .id = id }); try Drm.get_crtc.request(card.file.handle, RawCrtc, &raw); return .{ + .connector_ids = raw.connector_ids, + .count_connectors = raw.count_connectors, .id = raw.id, .buffer_id = raw.buffer_id, .x = raw.x, .y = raw.y, + .mode = raw.mode, .mode_valid = raw.mode_valid, .gamma_size = raw.gamma_size, .card = card, }; } - pub fn deinit(self: *Self) void { - _ = self; + pub fn attach(self: *Self, frame_buffer: *FrameBuffer, connector: *Connector, mode: Mode) !void { + self.connector_ids = &connector.id; + self.count_connectors = 1; + + var crtc = RawCrtc { + .id = self.id, + .x = self.x, + .y = self.y, + .buffer_id = frame_buffer.id, + .connector_ids = self.connector_ids, + .count_connectors = self.count_connectors, + .mode = mode, + .mode_valid = 1, + .gamma_size = self.gamma_size, + }; + + try Drm.set_crtc.request(self.card.file.handle, RawCrtc, &crtc); + + std.debug.print("crtc = {any}\n", .{self}); + } + + pub fn detach(self: *Self) void { + var crtc = RawCrtc { + .id = self.id, + .x = self.x, + .y = self.y, + .buffer_id = self.buffer_id, + .connector_ids = self.connector_ids, + .count_connectors = self.count_connectors, + .mode = self.mode, + .mode_valid = self.mode_valid, + .gamma_size = self.gamma_size, + }; + + Drm.set_crtc.request( + self.card.file.handle, + RawCrtc, + &crtc + ) catch |err| std.debug.panic("set crtc failed on detach: {}", .{err}); } }; diff --git a/src/screen/drm/frame-buffer/pixelformat.zig b/src/screen/drm/frame-buffer/pixelformat.zig new file mode 100644 index 0000000..a4b3f9a --- /dev/null +++ b/src/screen/drm/frame-buffer/pixelformat.zig @@ -0,0 +1,11 @@ + +fn pixel_format_code(comptime a: u8, comptime b: u8, comptime c: u8, comptime d: u8) u32 { + return @as(u32, @intCast(a)) | + (@as(u32, @intCast(b)) << 8) | + (@as(u32, @intCast(c)) << 16) | + (@as(u32, @intCast(d)) << 24); +} + +pub const PixelFormat = enum(u32) { + xrgb8888 = pixel_format_code('X', 'R', '2', '4'), +}; diff --git a/src/screen/drm/frame-buffer/root.zig b/src/screen/drm/frame-buffer/root.zig new file mode 100644 index 0000000..eb7b47b --- /dev/null +++ b/src/screen/drm/frame-buffer/root.zig @@ -0,0 +1,147 @@ +const std = @import("std"); +const os = std.os.linux; +const Card = @import("../card.zig").Card; +const Drm = @import("../request.zig").Drm; +const PixelFormat = @import("pixelformat.zig").PixelFormat; +const cerror = @import("../../cerror.zig"); + +const CreateDumb = extern struct { + height: u32, + width: u32, + bpp: u32, + flags: u32, + handle: u32, + pitch: u32, + size: u32, +}; + +const FrameBufferCmd2 = extern struct { + id: u32, + width: u32, + height: u32, + pixel_format: PixelFormat, + flags: u32, + handles: [4]u32, + pitches: [4]u32, + offsets: [4]u32, + modifier: [4]u32, +}; + +const MapDumb = extern struct { + handle: u32, + __padding: u32, + offset: i64, +}; + +pub const Pixel = packed struct(u32) { + blue: u8, + green: u8, + red: u8, + _padding: u8 = 0x0, +}; + +pub const FrameBuffer = struct { + const Self = @This(); + + card: *Card, + id: u32, + width: u32, + height: u32, + bpp: u32, + stride: u32, + data_size: u64, + canvas_size: u64, + handle: u32, + data: [*]volatile Pixel, + + pub fn init(card: *Card, width: u32, height: u32, bpp: u32) !Self { + var create_dumb = std.mem.zeroInit(CreateDumb, .{ + .width = width, + .height = height, + .bpp = bpp, + }); + try Drm.create_dumb.request( + card.file.handle, + CreateDumb, + &create_dumb + ); + + var fb = Self { + .id = 0, + .width = create_dumb.width, + .height = create_dumb.height, + .bpp = create_dumb.bpp, + .stride = create_dumb.pitch, + .data_size = create_dumb.size, + .canvas_size = create_dumb.size / (bpp / 8), + .handle = create_dumb.handle, + .data = undefined, + .card = card, + }; + + var buffer_cmd = FrameBufferCmd2 { + .id = 0, + .width = fb.width, + .height = fb.height, + .pixel_format = PixelFormat.xrgb8888, + .flags = 0, + .handles = [_] u32 { fb.handle, 0, 0, 0 }, + .pitches = [_] u32 { fb.stride, 0, 0, 0 }, + .offsets = [_] u32 { 0, 0, 0, 0 }, + .modifier = [_] u32 { 0, 0, 0, 0 }, + }; + + try Drm.add_frame_buffer2.request( + card.file.handle, + FrameBufferCmd2, + &buffer_cmd + ); + + fb.id = buffer_cmd.id; + + var map_dumb = std.mem.zeroInit(MapDumb, .{ .handle = fb.handle }); + + try Drm.map_dumb.request( + card.file.handle, + MapDumb, + &map_dumb + ); + + const address = os.mmap( + null, + fb.data_size, + os.PROT.READ | os.PROT.WRITE, + .{ .TYPE = os.MAP_TYPE.SHARED }, + card.file.handle, + map_dumb.offset + ); + + try cerror.from_usize(address); + + fb.data = @ptrFromInt(address); + + return fb; + } + + pub fn set(self: *Self, x: u32, y: u32, pixel: Pixel) void { + self.data[x + self.width * y] = pixel; + } + + pub fn deinit(self: *Self) void { + cerror.from_usize( + os.munmap(@ptrCast(@volatileCast(self.data)), self.data_size) + ) catch |err| std.debug.panic("munmap failed in frame-buffer: {}", .{err}); + + Drm.remove_frame_buffer.request( + self.card.file.handle, + u32, + &self.id + ) catch @panic("failed to remove frame-buffer"); + + Drm.destroy_dumb.request( + self.card.file.handle, + u32, + &self.handle + ) catch @panic("failed to destroy dumb-buffer"); + } +}; diff --git a/src/screen/drm/request.zig b/src/screen/drm/request.zig index f833c94..9e0a045 100644 --- a/src/screen/drm/request.zig +++ b/src/screen/drm/request.zig @@ -11,8 +11,14 @@ pub const Drm = enum(u8) { get_resources = 0xa0, get_crtc = 0xa1, + set_crtc = 0xa2, get_encoder = 0xa6, get_connector = 0xa7, + remove_frame_buffer = 0xaf, + create_dumb = 0xb2, + map_dumb = 0xb3, + destroy_dumb = 0xb4, + add_frame_buffer2 = 0xb8, pub fn request(self: Self, fd: os.fd_t, T: type, arg: *T) !void { const id = os.IOCTL.IOWR('d', @intFromEnum(self), T); diff --git a/src/screen/main.zig b/src/screen/main.zig index b0e5b2e..1a33fd0 100644 --- a/src/screen/main.zig +++ b/src/screen/main.zig @@ -5,16 +5,68 @@ pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = gpa.allocator(); - var card = try drm.Card.open("card1", allocator); + var card = try drm.Card.open("card0", allocator); defer card.close(); var resources = try card.resources(); defer resources.deinit(); - std.debug.print("id: {}\n", .{resources.crtc_ids[0]}); + var connector = try card.connector(resources.connector_ids[0]); + defer connector.deinit(); - var crtc = try card.crtc(resources.crtc_ids[0]); - defer crtc.deinit(); + std.debug.print("connector = {}\n", .{ connector.id }); - std.debug.print("crtc: {any}\n", .{crtc}); + const mode = connector.modes[0]; + + std.debug.print("mode = {s}@{d}Hz\n", .{ mode.name, mode.frame_rate() }); + + var crtc = try crtc: { + for (connector.encoder_ids) |encoder_id| { + var encoder = try card.encoder(encoder_id); + defer encoder.deinit(); + + for (resources.crtc_ids, 0..) |crtc_id, index| { + if ((encoder.possible_crtcs & (@as(u32, 1) << @intCast(index))) != 0) { + break :crtc card.crtc(crtc_id); + } + } + } + + break :crtc error.CrtcNotFound; + }; + defer crtc.detach(); + + std.debug.print("crtc = {}\n", .{ crtc.id }); + + var framebuffer = try card.create_frame_buffer(mode.horizontal.size, mode.vertical.size, 32); + defer framebuffer.deinit(); + + std.debug.print("framebuffer = {}, {}\n", .{ framebuffer.id, framebuffer.canvas_size }); + + try crtc.attach(&framebuffer, &connector, mode); + + var frame: u24 = 0; + + while (true) { + for (0..framebuffer.width) |x| { + for (0..framebuffer.height) |y| { + const raster_x = (frame + x) / 100 * 100; + const raster_y = y / 100 * 100; + const color = ((raster_x + raster_y) + 0x129453) * 0x329120; + framebuffer.set(@intCast(x), @intCast(y), .{ + .red = @intCast(color & 0xff), + .green = @intCast((color >> 8) & 0xff), + .blue = @intCast((color >> 16) & 0xff), + }); + } + } + + frame, _ = @addWithOverflow(frame, 1); + + try crtc.attach(&framebuffer, &connector, mode); + + std.time.sleep(@intFromFloat(1000000000 / mode.frame_rate())); + } + + while (true) { std.time.sleep(5000000000); } } |