zgpu

git clone git://git.electrosoup.com/zgpu
Log | Files | Refs | Submodules | README

commit f37e829d6938e793ee33c9785149d7932816f890
parent abb5a9b8c168813134595c2f3ccd09e3738b5059
Author: Christian Ermann <christianermann@gmail.com>
Date:   Fri, 18 Apr 2025 22:22:17 -0700

Create and release Surface

Diffstat:
Msrc/common.zig | 2+-
Asrc/glfw.zig | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/instance.zig | 12+++++++++++-
Msrc/main.zig | 54+++++++++++++++++++++++++-----------------------------
Asrc/surface.zig | 103+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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(...) +};