diff --git a/gpu/src/Adapter.zig b/gpu/src/Adapter.zig index a846122c..98cfbaed 100644 --- a/gpu/src/Adapter.zig +++ b/gpu/src/Adapter.zig @@ -36,6 +36,8 @@ limits: SupportedLimits, /// An adapter may be considered a fallback adapter if it has significant performance caveats in /// exchange for some combination of wider compatibility, more predictable behavior, or improved /// privacy. It is not guaranteed that a fallback adapter is available on every system. +/// +/// Always false on native implementations of WebGPU (TODO: why is this not queryable in Dawn?) fallback: bool, // The type erased pointer to the Adapter implementation diff --git a/gpu/src/Interface.zig b/gpu/src/Interface.zig index 4c67a081..d34204b4 100644 --- a/gpu/src/Interface.zig +++ b/gpu/src/Interface.zig @@ -1,6 +1,10 @@ //! A standard interface to a WebGPU implementation. //! //! Like std.mem.Allocator, but representing a WebGPU implementation. +const std = @import("std"); + +const Surface = @import("Surface.zig"); +const Adapter = @import("Adapter.zig"); const Interface = @This(); @@ -8,9 +12,13 @@ const Interface = @This(); ptr: *anyopaque, vtable: *const VTable, +// The @frameSize(func) of the implementations requestAdapter async function +request_adapter_frame_size: usize, + pub const VTable = struct { reference: fn (ptr: *anyopaque) void, release: fn (ptr: *anyopaque) void, + requestAdapter: fn requestAdapter(ptr: *anyopaque, options: *const RequestAdapterOptions) callconv(.Async) RequestAdapterResponse, }; pub inline fn reference(interface: Interface) void { @@ -21,5 +29,79 @@ pub inline fn release(interface: Interface) void { interface.vtable.release(interface.ptr); } -// TODO: -// WGPU_EXPORT void wgpuInstanceRequestAdapter(WGPUInstance instance, WGPURequestAdapterOptions const * options, WGPURequestAdapterCallback callback, void * userdata); +// TODO: docs +pub const RequestAdapterOptions = struct { + // TODO: + //power_preference: PowerPreference, + force_fallback_adapter: bool = false, + + /// Only respected by native WebGPU implementations. + compatible_surface: ?Surface, +}; + +pub const RequestAdapterErrorCode = error{ + Unavailable, + Error, + Unknown, +}; + +// TODO: docs +pub const RequestAdapterError = struct { + message: []const u8, + code: RequestAdapterErrorCode, +}; + +pub const RequestAdapterResponseTag = enum { + adapter, + err, +}; + +pub const RequestAdapterResponse = union(RequestAdapterResponseTag) { + // TODO: docs + adapter: Adapter, + err: RequestAdapterError, +}; + +// TODO: docs +pub fn requestAdapter(interface: Interface, options: *const RequestAdapterOptions) callconv(.Async) RequestAdapterResponse { + var frame_buffer = std.heap.page_allocator.allocAdvanced( + u8, + 16, + interface.request_adapter_frame_size, + std.mem.Allocator.Exact.at_least, + ) catch { + return .{ .err = .{ + .message = "Out of memory", + .code = RequestAdapterErrorCode.Error, + } }; + }; + defer std.heap.page_allocator.free(frame_buffer); + + var result: RequestAdapterResponse = undefined; + const f = @asyncCall(frame_buffer, &result, interface.vtable.requestAdapter, .{ interface.ptr, options }); + resume f; + return result; + + // @asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: anytype) anyframe->T + + // var data: i32 = 1; + // const Foo = struct { + // bar: fn (*i32) callconv(.Async) void, + // }; + // var foo = Foo{ .bar = func }; + // var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined; + // const f = @asyncCall(&bytes, {}, foo.bar, .{&data}); + + // return async interface.vtable.requestAdapter(interface.ptr, options); +} + +test "syntax" { + _ = VTable; + _ = reference; + _ = release; + _ = RequestAdapterOptions; + _ = RequestAdapterErrorCode; + _ = RequestAdapterError; + _ = RequestAdapterResponse; + _ = requestAdapter; +} diff --git a/gpu/src/NativeInstance.zig b/gpu/src/NativeInstance.zig index 15c73230..0aef01f4 100644 --- a/gpu/src/NativeInstance.zig +++ b/gpu/src/NativeInstance.zig @@ -1,6 +1,14 @@ //! A native webgpu.h implementation of the gpu.Interface +const std = @import("std"); const c = @import("c.zig").c; -const Interface = @import("Interface.zig"); + +pub const Interface = @import("Interface.zig"); +pub const RequestAdapterOptions = Interface.RequestAdapterOptions; +pub const RequestAdapterErrorCode = Interface.RequestAdapterErrorCode; +pub const RequestAdapterError = Interface.RequestAdapterError; +pub const RequestAdapterResponse = Interface.RequestAdapterResponse; + +pub const Adapter = @import("Adapter.zig"); const Surface = @import("Surface.zig"); const NativeInstance = @This(); @@ -8,6 +16,8 @@ const NativeInstance = @This(); /// The WGPUInstance that is wrapped by this native instance. instance: c.WGPUInstance, +request_adapter_response: ?Interface.RequestAdapterResponse = null, + /// Wraps a native WGPUInstance to provide an implementation of the gpu.Interface. pub fn wrap(instance: *anyopaque) NativeInstance { return .{ .instance = @ptrCast(c.WGPUInstance, instance) }; @@ -26,23 +36,73 @@ const interface_vtable = Interface.VTable{ c.wgpuInstanceRelease(native.instance); } }).release, + .requestAdapter = (struct { + pub fn requestAdapter(ptr: *anyopaque, options: *const RequestAdapterOptions) callconv(.Async) RequestAdapterResponse { + const native = @ptrCast(*NativeInstance, @alignCast(@alignOf(*NativeInstance), ptr)); + + const opt = c.WGPURequestAdapterOptions{ + .nextInChain = null, + .compatibleSurface = if (options.compatible_surface) |surface| @ptrCast(c.WGPUSurface, surface.ptr) else null, + // TODO: + //.powerPreference = power_preference, + .powerPreference = c.WGPUPowerPreference_Undefined, + .forceFallbackAdapter = options.force_fallback_adapter, + }; + + const callback = (struct { + pub fn callback(status: c.WGPURequestAdapterStatus, adapter: c.WGPUAdapter, message: [*c]const u8, userdata: ?*anyopaque) callconv(.C) void { + const _native = @ptrCast(*NativeInstance, @alignCast(@alignOf(*NativeInstance), userdata)); + + // Store the response into a field on the native instance for later reading. + _native.request_adapter_response = if (status == c.WGPURequestAdapterStatus_Success) .{ + .adapter = wrapAdapter(adapter.?), + } else .{ + .err = Interface.RequestAdapterError{ + .message = std.mem.span(message), + .code = switch (status) { + c.WGPURequestAdapterStatus_Unavailable => RequestAdapterErrorCode.Unavailable, + c.WGPURequestAdapterStatus_Error => RequestAdapterErrorCode.Error, + c.WGPURequestAdapterStatus_Unknown => RequestAdapterErrorCode.Unknown, + else => unreachable, + }, + }, + }; + } + }).callback; + + c.wgpuInstanceRequestAdapter(native.instance, &opt, callback, native); + // TODO: Once crbug.com/dawn/1122 is fixed, we should process events here otherwise our + // callback will not be invoked. + // c.wgpuInstanceProcessEvents(native.instance) + + // Return the response, asserting the callback has executed at this point. + const resp = native.request_adapter_response.?; + native.request_adapter_response = null; + return resp; + } + }).requestAdapter, }; +// TODO: +// typedef void (*WGPURequestAdapterCallback)(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * userdata); +// WGPU_EXPORT void wgpuInstanceRequestAdapter(WGPUInstance instance, WGPURequestAdapterOptions const * options, WGPURequestAdapterCallback callback, void * userdata); + +// typedef enum WGPURequestAdapterStatus { +// WGPURequestAdapterStatus_Success = 0x00000000, +// WGPURequestAdapterStatus_Unavailable = 0x00000001, +// WGPURequestAdapterStatus_Error = 0x00000002, +// WGPURequestAdapterStatus_Unknown = 0x00000003, +// WGPURequestAdapterStatus_Force32 = 0x7FFFFFFF +// } WGPURequestAdapterStatus; + /// Returns the gpu.Interface for interacting with this native instance. pub fn interface(native: *NativeInstance) Interface { + std.debug.assert(@alignOf(@Frame(interface_vtable.requestAdapter)) == 16); return .{ .ptr = native, .vtable = &interface_vtable, + .request_adapter_frame_size = @frameSize(interface_vtable.requestAdapter), }; - // TODO: implement Device interface - - // TODO: implement Adapter interface: - // typedef struct WGPUAdapterImpl* WGPUAdapter; - // // Methods of Adapter - // WGPU_EXPORT size_t wgpuAdapterEnumerateFeatures(WGPUAdapter adapter, WGPUFeatureName * features); - // WGPU_EXPORT bool wgpuAdapterHasFeature(WGPUAdapter adapter, WGPUFeatureName feature); - // WGPU_EXPORT bool wgpuAdapterGetLimits(WGPUAdapter adapter, WGPUSupportedLimits * limits); - // WGPU_EXPORT void wgpuAdapterGetProperties(WGPUAdapter adapter, WGPUAdapterProperties * properties); } pub fn createSurface(native: *const NativeInstance, descriptor: *const Surface.Descriptor) Surface { @@ -130,8 +190,36 @@ const surface_vtable = Surface.VTable{ }).release, }; +fn wrapAdapter(adapter: c.WGPUAdapter) Adapter { + // TODO: implement Adapter interface: + // WGPU_EXPORT size_t wgpuAdapterEnumerateFeatures(WGPUAdapter adapter, WGPUFeatureName * features); + // WGPU_EXPORT bool wgpuAdapterHasFeature(WGPUAdapter adapter, WGPUFeatureName feature); + // WGPU_EXPORT bool wgpuAdapterGetLimits(WGPUAdapter adapter, WGPUSupportedLimits * limits); + // WGPU_EXPORT void wgpuAdapterGetProperties(WGPUAdapter adapter, WGPUAdapterProperties * properties); + + return .{ + // TODO: + .features = undefined, + // TODO: + .limits = undefined, + + // TODO: why is fallback not queryable on Dawn? + .fallback = false, + + .ptr = adapter.?, + .vtable = &adapter_vtable, + }; +} + +const adapter_vtable = Adapter.VTable{}; + +// TODO: implement Device interface + test "syntax" { _ = wrap; + _ = interface_vtable; _ = interface; _ = createSurface; + _ = surface_vtable; + _ = adapter_vtable; } diff --git a/gpu/src/TODO b/gpu/src/TODO index 71e74e67..24596905 100644 --- a/gpu/src/TODO +++ b/gpu/src/TODO @@ -281,14 +281,6 @@ typedef enum WGPURenderPassTimestampLocation { WGPURenderPassTimestampLocation_Force32 = 0x7FFFFFFF } WGPURenderPassTimestampLocation; -typedef enum WGPURequestAdapterStatus { - WGPURequestAdapterStatus_Success = 0x00000000, - WGPURequestAdapterStatus_Unavailable = 0x00000001, - WGPURequestAdapterStatus_Error = 0x00000002, - WGPURequestAdapterStatus_Unknown = 0x00000003, - WGPURequestAdapterStatus_Force32 = 0x7FFFFFFF -} WGPURequestAdapterStatus; - typedef enum WGPURequestDeviceStatus { WGPURequestDeviceStatus_Success = 0x00000000, WGPURequestDeviceStatus_Error = 0x00000001, @@ -808,13 +800,6 @@ typedef struct WGPURenderPassTimestampWrite { WGPURenderPassTimestampLocation location; } WGPURenderPassTimestampWrite; -typedef struct WGPURequestAdapterOptions { - WGPUChainedStruct const * nextInChain; - WGPUSurface compatibleSurface; - WGPUPowerPreference powerPreference; - bool forceFallbackAdapter; -} WGPURequestAdapterOptions; - typedef struct WGPUSamplerBindingLayout { WGPUChainedStruct const * nextInChain; WGPUSamplerBindingType type; @@ -1112,7 +1097,6 @@ typedef void (*WGPUErrorCallback)(WGPUErrorType type, char const * message, void typedef void (*WGPULoggingCallback)(WGPULoggingType type, char const * message, void * userdata); typedef void (*WGPUProc)(); typedef void (*WGPUQueueWorkDoneCallback)(WGPUQueueWorkDoneStatus status, void * userdata); -typedef void (*WGPURequestAdapterCallback)(WGPURequestAdapterStatus status, WGPUAdapter adapter, char const * message, void * userdata); typedef void (*WGPURequestDeviceCallback)(WGPURequestDeviceStatus status, WGPUDevice device, char const * message, void * userdata); WGPU_EXPORT WGPUProc wgpuGetProcAddress(WGPUDevice device, char const * procName); diff --git a/gpu/src/main.zig b/gpu/src/main.zig index 06bfbe14..e26915c0 100644 --- a/gpu/src/main.zig +++ b/gpu/src/main.zig @@ -17,6 +17,11 @@ //! const std = @import("std"); pub const Interface = @import("Interface.zig"); +pub const RequestAdapterOptions = Interface.RequestAdapterOptions; +pub const RequestAdapterErrorCode = Interface.RequestAdapterErrorCode; +pub const RequestAdapterError = Interface.RequestAdapterError; +pub const RequestAdapterResponse = Interface.RequestAdapterResponse; + pub const NativeInstance = @import("NativeInstance.zig"); pub const Adapter = @import("Adapter.zig"); @@ -33,7 +38,6 @@ test "syntax" { _ = Adapter; _ = Device; _ = Surface; - _ = Surface.Descriptor; _ = FeatureName; _ = SupportedLimits;