all: move standalone libraries to libs/ subdirectory

The root dir of our repository has grown quite a lot the past few months.

I'd like to make it more clear where the bulk of the engine lives (`src/`) and
also make it more clear which Mach libraries are consumable as standalone projects.

As for the name of this directory, `libs` was my first choice but there's a bit of
a convention of that being external libraries in Zig projects _today_, while these
are libraries maintained as part of Mach in this repository - not external ones.

We will name this directory `libs`, and if we have a need for external libraries
we will use `external` or `deps` for that directory name. I considered other names
such as `components`, `systems`, `modules` (which are bad as they overlap with
major ECS / engine concepts), and it seems likely the official Zig package manager
will break the convention of using a `libs` dir anyway.

Performed via:

```sh
mkdir libs/
git mv freetype libs/
git mv basisu libs/
git mv gamemode libs/
git mv glfw libs/
git mv gpu libs/
git mv gpu-dawn libs/
git mv sysaudio libs/
git mv sysjs libs/
git mv ecs libs/
```

git-subtree-dir: glfw
git-subtree-mainline: 0d5b853443
git-subtree-split: 572d1144f11b353abdb64fff828b25a4f0fbb7ca

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>

git mv ecs libs/

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2022-08-26 13:29:04 -07:00 committed by Stephen Gutekanst
parent 79ec61396f
commit 0645429df9
240 changed files with 6 additions and 6 deletions

169
libs/gpu/examples/main.zig Normal file
View file

@ -0,0 +1,169 @@
const std = @import("std");
const sample_utils = @import("sample_utils.zig");
const glfw = @import("glfw");
const gpu = @import("gpu");
pub const GPUInterface = gpu.dawn.Interface;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var allocator = gpa.allocator();
gpu.Impl.init();
const setup = try sample_utils.setup(allocator);
const framebuffer_size = try setup.window.getFramebufferSize();
const window_data = try allocator.create(WindowData);
window_data.* = .{
.surface = setup.surface,
.swap_chain = null,
.swap_chain_format = undefined,
.current_desc = undefined,
.target_desc = undefined,
};
setup.window.setUserPointer(window_data);
window_data.swap_chain_format = .bgra8_unorm;
const descriptor = gpu.SwapChain.Descriptor{
.label = "basic swap chain",
.usage = .{ .render_attachment = true },
.format = window_data.swap_chain_format,
.width = framebuffer_size.width,
.height = framebuffer_size.height,
.present_mode = .fifo,
};
window_data.current_desc = descriptor;
window_data.target_desc = descriptor;
const vs =
\\ @vertex fn main(
\\ @builtin(vertex_index) VertexIndex : u32
\\ ) -> @builtin(position) vec4<f32> {
\\ var pos = array<vec2<f32>, 3>(
\\ vec2<f32>( 0.0, 0.5),
\\ vec2<f32>(-0.5, -0.5),
\\ vec2<f32>( 0.5, -0.5)
\\ );
\\ return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
\\ }
;
const vs_module = setup.device.createShaderModuleWGSL("my vertex shader", vs);
const fs =
\\ @fragment fn main() -> @location(0) vec4<f32> {
\\ return vec4<f32>(1.0, 0.0, 0.0, 1.0);
\\ }
;
const fs_module = setup.device.createShaderModuleWGSL("my fragment shader", fs);
// Fragment state
const blend = gpu.BlendState{
.color = .{
.dst_factor = .one,
},
.alpha = .{
.dst_factor = .one,
},
};
const color_target = gpu.ColorTargetState{
.format = window_data.swap_chain_format,
.blend = &blend,
.write_mask = gpu.ColorWriteMaskFlags.all,
};
const fragment = gpu.FragmentState.init(.{
.module = fs_module,
.entry_point = "main",
.targets = &.{color_target},
});
const pipeline_descriptor = gpu.RenderPipeline.Descriptor{
.fragment = &fragment,
.layout = null,
.depth_stencil = null,
.vertex = gpu.VertexState{
.module = vs_module,
.entry_point = "main",
},
.multisample = .{},
.primitive = .{},
};
const pipeline = setup.device.createRenderPipeline(&pipeline_descriptor);
vs_module.release();
fs_module.release();
// Reconfigure the swap chain with the new framebuffer width/height, otherwise e.g. the Vulkan
// device would be lost after a resize.
setup.window.setFramebufferSizeCallback((struct {
fn callback(window: glfw.Window, width: u32, height: u32) void {
const pl = window.getUserPointer(WindowData);
pl.?.target_desc.width = width;
pl.?.target_desc.height = height;
}
}).callback);
const queue = setup.device.getQueue();
while (!setup.window.shouldClose()) {
try frame(.{
.window = setup.window,
.device = setup.device,
.pipeline = pipeline,
.queue = queue,
});
std.time.sleep(16 * std.time.ns_per_ms);
}
}
const WindowData = struct {
surface: ?*gpu.Surface,
swap_chain: ?*gpu.SwapChain,
swap_chain_format: gpu.Texture.Format,
current_desc: gpu.SwapChain.Descriptor,
target_desc: gpu.SwapChain.Descriptor,
};
const FrameParams = struct {
window: glfw.Window,
device: *gpu.Device,
pipeline: *gpu.RenderPipeline,
queue: *gpu.Queue,
};
fn frame(params: FrameParams) !void {
const pool = try sample_utils.AutoReleasePool.init();
defer sample_utils.AutoReleasePool.release(pool);
try glfw.pollEvents();
const pl = params.window.getUserPointer(WindowData).?;
if (pl.swap_chain == null or !std.meta.eql(pl.current_desc, pl.target_desc)) {
pl.swap_chain = params.device.createSwapChain(pl.surface, &pl.target_desc);
pl.current_desc = pl.target_desc;
}
const back_buffer_view = pl.swap_chain.?.getCurrentTextureView();
const color_attachment = gpu.RenderPassColorAttachment{
.view = back_buffer_view,
.resolve_target = null,
.clear_value = std.mem.zeroes(gpu.Color),
.load_op = .clear,
.store_op = .store,
};
const encoder = params.device.createCommandEncoder(null);
const render_pass_info = gpu.RenderPassDescriptor.init(.{
.color_attachments = &.{color_attachment},
});
const pass = encoder.beginRenderPass(&render_pass_info);
pass.setPipeline(params.pipeline);
pass.draw(3, 1, 0, 0);
pass.end();
pass.release();
var command = encoder.finish(null);
encoder.release();
params.queue.submit(&.{command});
command.release();
pl.swap_chain.?.present();
back_buffer_view.release();
}

View file

@ -0,0 +1,9 @@
// Extracted from `zig translate-c tmp.c` with `#include <objc/message.h>` in the file.
pub const struct_objc_selector = opaque {};
pub const SEL = ?*struct_objc_selector;
pub const Class = ?*struct_objc_class;
pub const struct_objc_class = opaque {};
pub extern fn sel_getUid(str: [*c]const u8) SEL;
pub extern fn objc_getClass(name: [*c]const u8) Class;
pub extern fn objc_msgSend() void;

View file

@ -0,0 +1,262 @@
const std = @import("std");
const assert = std.debug.assert;
const glfw = @import("glfw");
const gpu = @import("gpu");
const objc = @import("objc_message.zig");
inline fn printUnhandledErrorCallback(_: void, typ: gpu.ErrorType, message: [*:0]const u8) void {
switch (typ) {
.validation => std.debug.print("gpu: validation error: {s}\n", .{message}),
.out_of_memory => std.debug.print("gpu: out of memory: {s}\n", .{message}),
.device_lost => std.debug.print("gpu: device lost: {s}\n", .{message}),
.unknown => std.debug.print("gpu: unknown error: {s}\n", .{message}),
else => unreachable,
}
std.process.exit(1);
}
const Setup = struct {
instance: *gpu.Instance,
adapter: *gpu.Adapter,
device: *gpu.Device,
window: glfw.Window,
surface: *gpu.Surface,
};
fn getEnvVarOwned(allocator: std.mem.Allocator, key: []const u8) error{ OutOfMemory, InvalidUtf8 }!?[]u8 {
return std.process.getEnvVarOwned(allocator, key) catch |err| switch (err) {
error.EnvironmentVariableNotFound => @as(?[]u8, null),
else => |e| e,
};
}
fn detectBackendType(allocator: std.mem.Allocator) !gpu.BackendType {
const MACH_GPU_BACKEND = try getEnvVarOwned(allocator, "MACH_GPU_BACKEND");
if (MACH_GPU_BACKEND) |backend| {
defer allocator.free(backend);
if (std.ascii.eqlIgnoreCase(backend, "null")) return .nul;
if (std.ascii.eqlIgnoreCase(backend, "webgpu")) return .nul;
if (std.ascii.eqlIgnoreCase(backend, "d3d11")) return .d3d11;
if (std.ascii.eqlIgnoreCase(backend, "d3d12")) return .d3d12;
if (std.ascii.eqlIgnoreCase(backend, "metal")) return .metal;
if (std.ascii.eqlIgnoreCase(backend, "vulkan")) return .vulkan;
if (std.ascii.eqlIgnoreCase(backend, "opengl")) return .opengl;
if (std.ascii.eqlIgnoreCase(backend, "opengles")) return .opengles;
@panic("unknown MACH_GPU_BACKEND type");
}
const target = @import("builtin").target;
if (target.isDarwin()) return .metal;
if (target.os.tag == .windows) return .d3d12;
return .vulkan;
}
const RequestAdapterResponse = struct {
status: gpu.RequestAdapterStatus,
adapter: *gpu.Adapter,
message: ?[*:0]const u8,
};
inline fn requestAdapterCallback(
context: *?RequestAdapterResponse,
status: gpu.RequestAdapterStatus,
adapter: *gpu.Adapter,
message: ?[*:0]const u8,
) void {
context.* = RequestAdapterResponse{
.status = status,
.adapter = adapter,
.message = message,
};
}
pub fn setup(allocator: std.mem.Allocator) !Setup {
const backend_type = try detectBackendType(allocator);
try glfw.init(.{});
// Create the test window and discover adapters using it (esp. for OpenGL)
var hints = glfwWindowHintsForBackend(backend_type);
hints.cocoa_retina_framebuffer = true;
const window = try glfw.Window.create(640, 480, "mach/gpu window", null, null, hints);
if (backend_type == .opengl) try glfw.makeContextCurrent(window);
if (backend_type == .opengles) try glfw.makeContextCurrent(window);
const instance = gpu.createInstance(null);
if (instance == null) {
std.debug.print("failed to create GPU instance\n", .{});
std.process.exit(1);
}
const surface = createSurfaceForWindow(instance.?, window, comptime detectGLFWOptions());
var response: ?RequestAdapterResponse = null;
instance.?.requestAdapter(&gpu.RequestAdapterOptions{
.compatible_surface = surface,
.power_preference = .undef,
.force_fallback_adapter = false,
}, &response, requestAdapterCallback);
if (response.?.status != .success) {
std.debug.print("failed to create GPU adapter: {s}\n", .{response.?.message.?});
std.process.exit(1);
}
// Print which adapter we are using.
var props: gpu.Adapter.Properties = undefined;
response.?.adapter.getProperties(&props);
std.debug.print("found {s} backend on {s} adapter: {s}, {s}\n", .{
props.backend_type.name(),
props.adapter_type.name(),
props.name,
props.driver_description,
});
// Create a device with default limits/features.
const device = response.?.adapter.createDevice(null);
if (device == null) {
std.debug.print("failed to create GPU device\n", .{});
std.process.exit(1);
}
device.?.setUncapturedErrorCallback({}, printUnhandledErrorCallback);
return Setup{
.instance = instance.?,
.adapter = response.?.adapter,
.device = device.?,
.window = window,
.surface = surface,
};
}
fn glfwWindowHintsForBackend(backend: gpu.BackendType) glfw.Window.Hints {
return switch (backend) {
.opengl => .{
// Ask for OpenGL 4.4 which is what the GL backend requires for compute shaders and
// texture views.
.context_version_major = 4,
.context_version_minor = 4,
.opengl_forward_compat = true,
.opengl_profile = .opengl_core_profile,
},
.opengles => .{
.context_version_major = 3,
.context_version_minor = 1,
.client_api = .opengl_es_api,
.context_creation_api = .egl_context_api,
},
else => .{
// Without this GLFW will initialize a GL context on the window, which prevents using
// the window with other APIs (by crashing in weird ways).
.client_api = .no_api,
},
};
}
pub fn detectGLFWOptions() glfw.BackendOptions {
const target = @import("builtin").target;
if (target.isDarwin()) return .{ .cocoa = true };
return switch (target.os.tag) {
.windows => .{ .win32 = true },
.linux => .{ .x11 = true },
else => .{},
};
}
pub fn createSurfaceForWindow(
instance: *gpu.Instance,
window: glfw.Window,
comptime glfw_options: glfw.BackendOptions,
) *gpu.Surface {
const glfw_native = glfw.Native(glfw_options);
const extension = if (glfw_options.win32) gpu.Surface.Descriptor.NextInChain{
.from_windows_hwnd = &.{
.hinstance = std.os.windows.kernel32.GetModuleHandleW(null).?,
.hwnd = glfw_native.getWin32Window(window),
},
} else if (glfw_options.x11) gpu.Surface.Descriptor.NextInChain{
.from_xlib_window = &.{
.display = glfw_native.getX11Display(),
.window = glfw_native.getX11Window(window),
},
} else if (glfw_options.cocoa) blk: {
const ns_window = glfw_native.getCocoaWindow(window);
const ns_view = msgSend(ns_window, "contentView", .{}, *anyopaque); // [nsWindow contentView]
// Create a CAMetalLayer that covers the whole window that will be passed to CreateSurface.
msgSend(ns_view, "setWantsLayer:", .{true}, void); // [view setWantsLayer:YES]
const layer = msgSend(objc.objc_getClass("CAMetalLayer"), "layer", .{}, ?*anyopaque); // [CAMetalLayer layer]
if (layer == null) @panic("failed to create Metal layer");
msgSend(ns_view, "setLayer:", .{layer.?}, void); // [view setLayer:layer]
// Use retina if the window was created with retina support.
const scale_factor = msgSend(ns_window, "backingScaleFactor", .{}, f64); // [ns_window backingScaleFactor]
msgSend(layer.?, "setContentsScale:", .{scale_factor}, void); // [layer setContentsScale:scale_factor]
break :blk gpu.Surface.Descriptor.NextInChain{ .from_metal_layer = &.{ .layer = layer.? } };
} else if (glfw_options.wayland) {
@panic("TODO: this example does not support Wayland");
} else unreachable;
return instance.createSurface(&gpu.Surface.Descriptor{
.next_in_chain = extension,
});
}
pub const AutoReleasePool = if (!@import("builtin").target.isDarwin()) opaque {
pub fn init() error{OutOfMemory}!?*AutoReleasePool {
return null;
}
pub fn release(pool: ?*AutoReleasePool) void {
_ = pool;
return;
}
} else opaque {
pub fn init() error{OutOfMemory}!?*AutoReleasePool {
// pool = [NSAutoreleasePool alloc];
var pool = msgSend(objc.objc_getClass("NSAutoreleasePool"), "alloc", .{}, ?*AutoReleasePool);
if (pool == null) return error.OutOfMemory;
// pool = [pool init];
pool = msgSend(pool, "init", .{}, ?*AutoReleasePool);
if (pool == null) unreachable;
return pool;
}
pub fn release(pool: ?*AutoReleasePool) void {
// [pool release];
msgSend(pool, "release", .{}, void);
}
};
// Borrowed from https://github.com/hazeycode/zig-objcrt
pub fn msgSend(obj: anytype, sel_name: [:0]const u8, args: anytype, comptime ReturnType: type) ReturnType {
const args_meta = @typeInfo(@TypeOf(args)).Struct.fields;
const FnType = if (@import("builtin").zig_backend == .stage1)
switch (args_meta.len) {
0 => fn (@TypeOf(obj), objc.SEL) callconv(.C) ReturnType,
1 => fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type) callconv(.C) ReturnType,
2 => fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type, args_meta[1].field_type) callconv(.C) ReturnType,
3 => fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type, args_meta[1].field_type, args_meta[2].field_type) callconv(.C) ReturnType,
4 => fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type, args_meta[1].field_type, args_meta[2].field_type, args_meta[3].field_type) callconv(.C) ReturnType,
else => @compileError("Unsupported number of args"),
}
else switch (args_meta.len) {
0 => *const fn (@TypeOf(obj), objc.SEL) callconv(.C) ReturnType,
1 => *const fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type) callconv(.C) ReturnType,
2 => *const fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type, args_meta[1].field_type) callconv(.C) ReturnType,
3 => *const fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type, args_meta[1].field_type, args_meta[2].field_type) callconv(.C) ReturnType,
4 => *const fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type, args_meta[1].field_type, args_meta[2].field_type, args_meta[3].field_type) callconv(.C) ReturnType,
else => @compileError("Unsupported number of args"),
};
// NOTE: func is a var because making it const causes a compile error which I believe is a compiler bug
var func = if (@import("builtin").zig_backend == .stage1)
@ptrCast(FnType, objc.objc_msgSend)
else
@ptrCast(FnType, &objc.objc_msgSend);
const sel = objc.sel_getUid(@ptrCast([*c]const u8, sel_name));
return @call(.{}, func, .{ obj, sel } ++ args);
}