Something went wrong. Try again.
Windows, a GPU renderer and text for Zig programs that draw their own interface.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! Where a window's finished frames go: on Wayland, dma-bufs the compositor//! reads directly where it and the GPU can share them, and shared memory//! where they cannot; on Windows, a swapchain on the window's surface.
const std = @import("std");const builtin = @import("builtin");
const Device = @import("Device.zig");const Renderer = @import("Renderer.zig");const platform = @import("../platform/platform.zig");const Dmabuf = @import("wayland/Dmabuf.zig");const Shm = @import("wayland/Shm.zig");const Swapchain = @import("win32/Swapchain.zig");const Atlas = @import("../text/Atlas.zig");const testing_ = @import("../testing.zig");
const Target = @This();
const log = std.log.scoped(.present);
impl: switch (builtin.os.tag) { .windows => union(enum) { swapchain: *Swapchain }, else => union(enum) { dmabuf: *Dmabuf, shm: Shm, },},
/// How frames reach the compositor. Windows has only its swapchain, and/// takes no notice.pub const Mode = enum { /// Dma-bufs if the compositor takes one, and shared memory otherwise. auto, /// Dma-bufs, or nothing. dmabuf, /// Shared memory, always: a copy back from the GPU every frame. shm,};
/// A target for `window`, which is `width` by `height` physical pixels:/// the size a dma-buf is tried at, to see whether the compositor takes it./// With `.auto`, a compositor or device that cannot share dma-bufs, or a/// compositor that refuses the one tried, means shared memory instead;/// with `.dmabuf`, the reason is returned. On Windows, a swapchain, and/// `mode`, `width` and `height` are not used.pub fn init(device: *Device, window: *platform.Window, width: u32, height: u32, mode: Mode) !Target { if (builtin.os.tag == .windows) return .{ .impl = .{ .swapchain = try Swapchain.create(device, window.native()) } }; if (mode != .shm) dmabuf: { const d = Dmabuf.create(device, window.native()) catch |err| { if (mode == .dmabuf) return err; log.info("presenting through shared memory: {t}", .{err}); break :dmabuf; }; d.probe(width, height) catch |err| { d.destroy(); if (mode == .dmabuf) return err; log.info("presenting through shared memory: the compositor refused a dma-buf ({t})", .{err}); break :dmabuf; }; return .{ .impl = .{ .dmabuf = d } }; } return .{ .impl = .{ .shm = try .init(device, window.native()) } };}
test init { try testing_.live(); const gpa = std.testing.allocator; var env = try std.testing.environ.createMap(gpa); defer env.deinit(); const plat = try platform.Platform.init(gpa, std.testing.io, &env, .{ .app_id = "dev.jcollie.aerie-test" }); defer plat.deinit(); const device = try Device.init(gpa, .{ .application_name = "aerie doctest", .main_device = plat.mainDevice() }); defer device.deinit(); const window = try platform.Window.create(plat, .{ .title = "Target.init", .width = 320, .height = 200, .user = null }); defer window.destroy();
const size = window.bufferSize(); var target_ = try Target.init(device, window, size.width, size.height, .auto); defer target_.deinit();}
/// Lets go of the dma-bufs, shared memory or swapchain, once the GPU is/// done with them. Before the window and the device.pub fn deinit(t: *Target) void { switch (builtin.os.tag) { .windows => t.impl.swapchain.destroy(), else => switch (t.impl) { .dmabuf => |d| d.destroy(), .shm => |*s| s.deinit(), }, } t.* = undefined;}
test deinit { try testing_.live(); const gpa = std.testing.allocator; var env = try std.testing.environ.createMap(gpa); defer env.deinit(); const plat = try platform.Platform.init(gpa, std.testing.io, &env, .{ .app_id = "dev.jcollie.aerie-test" }); defer plat.deinit(); const device = try Device.init(gpa, .{ .application_name = "aerie doctest", .main_device = plat.mainDevice() }); defer device.deinit(); const window = try platform.Window.create(plat, .{ .title = "Target.deinit", .width = 320, .height = 200, .user = null }); defer window.destroy();
// Shared memory, whatever the compositor can do. var target_ = try Target.init(device, window, 320, 200, .shm); target_.deinit();}
/// What `Renderer.draw` hands the frame to.pub fn target(t: *Target) Renderer.Target { return switch (builtin.os.tag) { .windows => t.impl.swapchain.target(), else => switch (t.impl) { .dmabuf => |d| d.target(), .shm => |*s| s.target(), }, };}
test target { try testing_.live(); const gpa = std.testing.allocator; var env = try std.testing.environ.createMap(gpa); defer env.deinit(); const plat = try platform.Platform.init(gpa, std.testing.io, &env, .{ .app_id = "dev.jcollie.aerie-test" }); defer plat.deinit(); const device = try Device.init(gpa, .{ .application_name = "aerie doctest", .main_device = plat.mainDevice() }); defer device.deinit(); var renderer = try Renderer.init(device); defer renderer.deinit(); var alpha = try Atlas.init(gpa, 64, .alpha8); defer alpha.deinit(gpa); var color = try Atlas.init(gpa, 64, .rgba); defer color.deinit(gpa); const window = try platform.Window.create(plat, .{ .title = "Target.target", .width = 320, .height = 200, .user = null }); defer window.destroy();
const size = window.bufferSize(); var target_ = try Target.init(device, window, size.width, size.height, .auto); defer target_.deinit();
// One frame: a blue square on white. const square = [_]Renderer.Instance{.{ .x = 20, .y = 20, .width = 100, .height = 100, .color = .{ 0x2f, 0x6f, 0xb3, 255 }, .kind = .solid }}; try renderer.draw(size.width, size.height, .{ 1, 1, 1, 1 }, &square, &.{}, &.{}, &alpha, &color, target_.target());}