commit f37e829d6938e793ee33c9785149d7932816f890
parent abb5a9b8c168813134595c2f3ccd09e3738b5059
Author: Christian Ermann <christianermann@gmail.com>
Date: Fri, 18 Apr 2025 22:22:17 -0700
Create and release Surface
Diffstat:
5 files changed, 239 insertions(+), 31 deletions(-)
diff --git a/src/common.zig b/src/common.zig
@@ -20,5 +20,5 @@ pub const SType = enum(u32) {
pub const ChainedStruct = extern struct {
next: ?*const ChainedStruct,
- s_type: SType,
+ struct_type: SType,
};
diff --git a/src/glfw.zig b/src/glfw.zig
@@ -0,0 +1,99 @@
+const builtin = @import("builtin");
+
+const c = @cImport({
+ @cInclude("glfw3.h");
+ if (builtin.target.os.tag == .linux) {
+ @cDefine("GLFW_EXPOSE_NATIVE_X11", "1");
+ }
+ @cInclude("glfw3native.h");
+});
+
+const Error = error{
+ NotInitialized,
+ NoCurrentContext,
+ InvalidEnum,
+ InvalidValue,
+ OutOfMemory,
+ APIUnavailable,
+ VersionUnavailable,
+ PlatformError,
+ FormatUnavailable,
+ NoWindowContext,
+ Unknown,
+};
+
+const Monitor = opaque {};
+const Window = opaque {};
+
+fn getError() !void {
+ const error_code = c.glfwGetError(null);
+ return switch (error_code) {
+ c.GLFW_NO_ERROR => {},
+ c.GLFW_NOT_INITIALIZED => Error.NotInitialized,
+ c.GLFW_NO_CURRENT_CONTEXT => Error.NoCurrentContext,
+ c.GLFW_INVALID_ENUM => Error.InvalidEnum,
+ c.GLFW_INVALID_VALUE => Error.InvalidValue,
+ c.GLFW_OUT_OF_MEMORY => Error.OutOfMemory,
+ c.GLFW_API_UNAVAILABLE => Error.APIUnavailable,
+ c.GLFW_VERSION_UNAVAILABLE => Error.VersionUnavailable,
+ c.GLFW_PLATFORM_ERROR => Error.PlatformError,
+ c.GLFW_FORMAT_UNAVAILABLE => Error.FormatUnavailable,
+ c.GLFW_NO_WINDOW_CONTEXT => Error.NoWindowContext,
+ else => unreachable,
+ };
+}
+
+pub fn init() !void {
+ const status = c.glfwInit();
+ return switch (status) {
+ c.GLFW_TRUE => {},
+ c.GLFW_FALSE => getError(),
+ else => unreachable,
+ };
+}
+
+pub fn terminate() void {
+ c.glfwTerminate();
+}
+
+pub fn createWindow(
+ width: c_int,
+ height: c_int,
+ title: [*:0]const u8,
+ monitor: ?*Monitor,
+ share: ?*Window,
+) !*Window {
+ const window = c.glfwCreateWindow(
+ width,
+ height,
+ title,
+ @ptrCast(monitor),
+ @ptrCast(share),
+ );
+ try getError();
+ return @ptrCast(window.?);
+}
+
+pub fn destroyWindow(window: *Window) void {
+ c.glfwDestroyWindow(@ptrCast(window));
+}
+
+pub const native = switch (builtin.target.os.tag) {
+ .linux => struct {
+ const X11Display = opaque {};
+ const X11Window = u64;
+
+ pub fn getX11Display() !*X11Display {
+ const display = c.glfwGetX11Display();
+ if (display == null) {
+ try getError();
+ }
+ return @ptrCast(display.?);
+ }
+
+ pub fn getX11Window(window: *Window) !X11Window {
+ return @intCast(c.glfwGetX11Window(@ptrCast(window)));
+ }
+ },
+ else => error.UnsupportedOS,
+};
diff --git a/src/instance.zig b/src/instance.zig
@@ -3,6 +3,7 @@ const c = @cImport({
});
const ChainedStruct = @import("common.zig").ChainedStruct;
+const Surface = @import("surface.zig").Surface;
pub const Instance = opaque {
pub const Descriptor = extern struct {
@@ -17,7 +18,16 @@ pub const Instance = opaque {
c.wgpuInstanceRelease(@ptrCast(instance));
}
- // createSurface(...)
+ pub fn createSurface(
+ instance: *Instance,
+ descriptor: *const Surface.Descriptor,
+ ) ?*Surface {
+ return @ptrCast(c.wgpuInstanceCreateSurface(
+ @ptrCast(instance),
+ @ptrCast(descriptor),
+ ));
+ }
+
// hasWGSLLanguageFeature(...)
// requestAdapter(...)
// reference(...)
diff --git a/src/main.zig b/src/main.zig
@@ -1,37 +1,33 @@
-//! By convention, main.zig is where your main function lives in the case that
-//! you are building an executable. If you are making a library, the convention
-//! is to delete this file and start with root.zig instead.
const std = @import("std");
-pub fn main() !void {
- // Prints to stderr (it's a shortcut based on `std.io.getStdErr()`)
- std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
-
- // stdout is for the actual output of your application, for example if you
- // are implementing gzip, then only the compressed bytes should be sent to
- // stdout, not any debugging messages.
- const stdout_file = std.io.getStdOut().writer();
- var bw = std.io.bufferedWriter(stdout_file);
- const stdout = bw.writer();
+const glfw = @import("glfw.zig");
+const Instance = @import("instance.zig").Instance;
- try stdout.print("Run `zig build test` to run the tests.\n", .{});
+pub fn main() !void {
+ try glfw.init();
+ defer glfw.terminate();
- try bw.flush(); // Don't forget to flush!
-}
+ const window = try glfw.createWindow(640, 480, "test", null, null);
+ defer glfw.destroyWindow(window);
+ const display = try glfw.native.getX11Display();
+ const window_id = try glfw.native.getX11Window(window);
-test "simple test" {
- var list = std.ArrayList(i32).init(std.testing.allocator);
- defer list.deinit(); // Try commenting this out and see if zig detects the memory leak!
- try list.append(42);
- try std.testing.expectEqual(@as(i32, 42), list.pop());
-}
+ const instance = Instance.create(null) orelse {
+ std.log.err("failed to create GPU instance", .{});
+ std.process.exit(1);
+ };
+ defer instance.release();
-test "fuzz example" {
- const global = struct {
- fn testOne(input: []const u8) anyerror!void {
- // Try passing `--fuzz` to `zig build test` and see if it manages to fail this test case!
- try std.testing.expect(!std.mem.eql(u8, "canyoufindme", input));
- }
+ const surface = instance.createSurface(&.{
+ .next = .{
+ .from_xlib_window = &.{
+ .window = window_id,
+ .display = display,
+ },
+ },
+ }) orelse {
+ std.log.err("failed to create GPU surface", .{});
+ std.process.exit(1);
};
- try std.testing.fuzz(global.testOne, .{});
+ defer surface.release();
}
diff --git a/src/surface.zig b/src/surface.zig
@@ -0,0 +1,103 @@
+const c = @cImport({
+ @cInclude("webgpu.h");
+});
+
+const ChainedStruct = @import("common.zig").ChainedStruct;
+
+// WGPUSurface
+// WGPUSurfaceDescriptor
+// WGPUSurfaceDescriptorFromAndroidNativeWindow
+// WGPUSurfaceDescriptorFromCanvasHTMLSelector
+// WGPUSurfaceDescriptorFromMetalLayer
+// WGPUSurfaceDescriptorFromWaylandSurface
+// WGPUSurfaceDescriptorFromWindowsHWND
+// WGPUSurfaceDescriptorFromXcbWindow
+// WGPUSurfaceDescriptorFromXlibWindow
+pub const Surface = opaque {
+ pub const Descriptor = extern struct {
+ pub const Next = extern union {
+ generic: ?*const ChainedStruct,
+ from_android_native_window: *const DescriptorFromAndroidNativeWindow,
+ from_canvas_html_selector: *const DescriptorFromCanvasHTMLSelector,
+ from_metal_layer: *const DescriptorFromMetalLayer,
+ from_wayland_surface: *const DescriptorFromWaylandSurface,
+ from_windows_hwnd: *const DescriptorFromWindowsHWND,
+ from_xcb_window: *const DescriptorFromXcbWindow,
+ from_xlib_window: *const DescriptorFromXlibWindow,
+ };
+ next: Next = .{ .generic = null },
+ label: ?[*:0]const u8 = null,
+ };
+
+ pub const DescriptorFromAndroidNativeWindow = extern struct {
+ chain: ChainedStruct = .{
+ .next = null,
+ .struct_type = .surface_descriptor_from_android_native_window,
+ },
+ window: *anyopaque,
+ };
+
+ pub const DescriptorFromCanvasHTMLSelector = extern struct {
+ chain: ChainedStruct = .{
+ .next = null,
+ .struct_type = .surface_descriptor_from_canvas_html_selector,
+ },
+ selector: [*:0]const u8,
+ };
+
+ pub const DescriptorFromMetalLayer = extern struct {
+ chain: ChainedStruct = .{
+ .next = null,
+ .struct_type = .surface_descriptor_from_metal_layer,
+ },
+ layer: *anyopaque,
+ };
+
+ pub const DescriptorFromWaylandSurface = extern struct {
+ chain: ChainedStruct = .{
+ .next = null,
+ .struct_type = .surface_descriptor_from_wayland_surface,
+ },
+ display: *anyopaque,
+ surface: *anyopaque,
+ };
+
+ pub const DescriptorFromWindowsHWND = extern struct {
+ chain: ChainedStruct = .{
+ .next = null,
+ .struct_type = .surface_descriptor_from_windows_hwnd,
+ },
+ hinstance: *anyopaque,
+ hwnd: *anyopaque,
+ };
+
+ pub const DescriptorFromXcbWindow = extern struct {
+ chain: ChainedStruct = .{
+ .next = null,
+ .struct_type = .surface_descriptor_from_xcb_window,
+ },
+ connection: *anyopaque,
+ window: u32,
+ };
+
+ pub const DescriptorFromXlibWindow = extern struct {
+ chain: ChainedStruct = .{
+ .next = null,
+ .struct_type = .surface_descriptor_from_xlib_window,
+ },
+ display: *anyopaque,
+ window: u64,
+ };
+
+ pub fn release(surface: *Surface) void {
+ c.wgpuSurfaceRelease(@ptrCast(surface));
+ }
+
+ // configure(...)
+ // getCapabilities(...)
+ // getCurrentTexture(...)
+ // present(...)
+ // setLabel(...)
+ // unconfigure(...)
+ // reference(...)
+};