diff --git a/README.md b/README.md index b1a51f0..2653aee 100644 --- a/README.md +++ b/README.md @@ -197,17 +197,15 @@ b.getInstallStep().dependOn(&install_dll.step); }).withDesiredMaxFrameLatency(2); ``` * `WGPUBool` is replaced with `bool` whenever possible. - * This pretty much means, it is replaced with `bool` in the parameters and return values of methods, but not in structs or the parameters/return values of procs (which are supposed to be function pointers to things returned by `wgpuGetProcAddress`). + * This pretty much means, it is replaced with `bool` in the parameters and return values of methods, but not in structs ## TODO * Test this on other machines with different OS/CPU. Currently only tested on x86_64-linux-gnu and x86_64-windows (msvc and gnu); zig version 0.14.0. * Cleanup/organization: * If types are only tied to a specific opaque struct, they should be decls inside that struct. - * The associated Procs struct should probably be a decl of the opaque struct as well. * There are many things that seem to be in the wrong file. * For example a lot of what is in `pipeline.zig` is actually only used by `Device`, and should probably be in `device.zig` instead. * Since pointers to opaque structs are made explicit, it would be more consistent if pointers to callback functions are explicit as well. * Port [wgpu-native-examples](https://github.com/samdauwe/webgpu-native-examples) using wrapper code, as a basic form of documentation. * Custom-build `wgpu-native`; provided all the necessary tools/dependencies are present. * Bindgen using [the webgpu-headers yaml](https://github.com/webgpu-native/webgpu-headers/blob/main/webgpu.yml)? -* The proc definitions are mainly there since they are also present in the webgpu headers and I didn't fully understand what they were for when I started working on this project. However, I know better now and they aren't really used for anything currently. They're supposed to be used with `wgpuGetProcAddress` but it's [unimplemented in `wgpu-native`](https://github.com/gfx-rs/wgpu-native/issues/223). They are a pain to update by hand, so maybe they should be removed for now and made optional once we have a working bindings generator? Like the bindgen could put them in a separate `wgpu-procs` module. diff --git a/build.zig b/build.zig index 421a8da..bc47bc5 100644 --- a/build.zig +++ b/build.zig @@ -263,64 +263,50 @@ fn triangle_example(b: *std.Build, context: *const WGPUBuildContext) void { } fn unit_tests(b: *std.Build, context: *const WGPUBuildContext) void { - const unit_test_step = b.step("test", "Run unit tests"); + const unit_test_step = b.step("unit-tests", "Run unit tests"); if (context.is_windows) { unit_test_step.dependOn(b.getInstallStep()); } - const test_files = [_][:0]const u8{ - "src/instance.zig", - "src/adapter.zig", - "src/pipeline.zig", - }; - comptime var test_names: [test_files.len][:0]const u8 = test_files; - comptime for (test_files, 0..) |test_file, idx| { - const test_name = test_file[4..(test_file.len - 4)] ++ "-test"; - test_names[idx] = test_name; - }; - - for (test_files, test_names) |test_file, test_name| { - // TODO: Seems weird to have a mod for each unit test, should probably revisit this. - const test_mod = b.createModule(.{ - .root_source_file = b.path(test_file), - .target = context.target, - .optimize = context.optimize, - }); - const t = b.addTest(.{ - .name = test_name, - .root_module = test_mod, - }); - handle_rt(context, t); - if (context.libwgpu_path != null) { - t.addObjectFile(context.libwgpu_path.?); - } - if (context.is_windows) { - t.linkLibC(); - } else { - t.linkLibCpp(); - } + const test_mod = b.createModule(.{ + .root_source_file = b.path("src/root.zig"), + .target = context.target, + .optimize = context.optimize, + }); + const t = b.addTest(.{ + .name = "root_unit", + .root_module = test_mod, + }); + handle_rt(context, t); + if (context.libwgpu_path != null) { + t.addObjectFile(context.libwgpu_path.?); + } + if (context.is_windows) { + t.linkLibC(); + } else { + t.linkLibCpp(); + } - const run_test = b.addRunArtifact(t); + const run_test = b.addRunArtifact(t); - if (context.is_mac) { - link_mac_frameworks(t); - } + if (context.is_mac) { + link_mac_frameworks(t); + } - if (context.link_mode == .dynamic) { - dynamic_link(context, t, run_test); - } else if (context.is_windows) { - if (context.target.result.abi == .gnu) { - link_windows_system_libraries(std.Build.Step.Compile, t, true); + if (context.link_mode == .dynamic) { + dynamic_link(context, t, run_test); + } else if (context.is_windows) { + if (context.target.result.abi == .gnu) { + link_windows_system_libraries(std.Build.Step.Compile, t, true); - // TODO: Find out why this is only required here; seems suspicious - t.linkSystemLibrary2("unwind", .{}); - } else { - link_windows_system_libraries(std.Build.Step.Compile, t, false); - } + // TODO: Find out why this is only required here; seems suspicious + t.linkSystemLibrary2("unwind", .{}); + } else { + link_windows_system_libraries(std.Build.Step.Compile, t, false); } - - unit_test_step.dependOn(&run_test.step); } + + unit_test_step.dependOn(&run_test.step); } fn compute_tests(b: *std.Build, context: *const WGPUBuildContext) void { diff --git a/examples/triangle/triangle.zig b/examples/triangle/triangle.zig index f0fbf25..e544c74 100644 --- a/examples/triangle/triangle.zig +++ b/examples/triangle/triangle.zig @@ -19,36 +19,28 @@ fn handleBufferMap(status: wgpu.MapAsyncStatus, _: wgpu.StringView, userdata1: ? // Based off of headless triangle example from https://github.com/eliemichel/LearnWebGPU-Code/tree/step030-headless pub fn main() !void { - const instance = wgpu.Instance.create(null).?; + const instance = try wgpu.Instance.create(null); defer instance.release(); - const adapter_request = instance.requestAdapterSync(&wgpu.RequestAdapterOptions {}, 0); - const adapter = switch(adapter_request.status) { - .success => adapter_request.adapter.?, - else => return error.NoAdapter, - }; + const adapter = try instance.requestAdapterSync(wgpu.RequestAdapterOptions {}, 0); defer adapter.release(); - const device_request = adapter.requestDeviceSync(instance, &wgpu.DeviceDescriptor { + const device = try adapter.requestDeviceSync(instance, .{ .required_limits = null, }, 0); - const device = switch(device_request.status) { - .success => device_request.device.?, - else => return error.NoDevice, - }; defer device.release(); - const queue = device.getQueue().?; + const queue = try device.getQueue(); defer queue.release(); const swap_chain_format = wgpu.TextureFormat.bgra8_unorm_srgb; - const target_texture = device.createTexture(&wgpu.TextureDescriptor { + const target_texture = try device.createTexture(&wgpu.TextureDescriptor { .label = wgpu.StringView.fromSlice("Render texture"), .size = output_extent, .format = swap_chain_format, - .usage = wgpu.TextureUsages.render_attachment | wgpu.TextureUsages.copy_src, - }).?; + .usage = wgpu.TextureUsage{ .render_attachment = true, .copy_src = true }, + }); defer target_texture.release(); const target_texture_view = target_texture.createView(&wgpu.TextureViewDescriptor { @@ -57,17 +49,17 @@ pub fn main() !void { .array_layer_count = 1, }).?; - const shader_module = device.createShaderModule(&wgpu.shaderModuleWGSLDescriptor(.{ + const shader_module = try device.createShaderModule(&wgpu.shaderModuleWGSLDescriptor(.{ .code = @embedFile("./shader.wgsl"), - })).?; + })); defer shader_module.release(); - const staging_buffer = device.createBuffer(&wgpu.BufferDescriptor { + const staging_buffer = try device.createBuffer(&wgpu.BufferDescriptor { .label = wgpu.StringView.fromSlice("staging_buffer"), - .usage = wgpu.BufferUsages.map_read | wgpu.BufferUsages.copy_dst, + .usage = wgpu.BufferUsage{ .map_read = true, .copy_dst = true }, .size = output_size, .mapped_at_creation = @as(u32, @intFromBool(false)), - }).?; + }); defer staging_buffer.release(); const color_targets = &[_] wgpu.ColorTargetState{ @@ -88,7 +80,7 @@ pub fn main() !void { }, }; - const pipeline = device.createRenderPipeline(&wgpu.RenderPipelineDescriptor { + const pipeline = try device.createRenderPipeline(&wgpu.RenderPipelineDescriptor { .vertex = wgpu.VertexState { .module = shader_module, .entry_point = wgpu.StringView.fromSlice("vs_main"), @@ -101,15 +93,15 @@ pub fn main() !void { .targets = color_targets.ptr }, .multisample = wgpu.MultisampleState {}, - }).?; + }); defer pipeline.release(); { // Mock main "loop" const next_texture = target_texture_view; - const encoder = device.createCommandEncoder(&wgpu.CommandEncoderDescriptor { + const encoder = try device.createCommandEncoder(&wgpu.CommandEncoderDescriptor { .label = wgpu.StringView.fromSlice("Command Encoder"), - }).?; + }); defer encoder.release(); const color_attachments = &[_]wgpu.ColorAttachment{ @@ -155,7 +147,7 @@ pub fn main() !void { queue.submit(&[_]*const wgpu.CommandBuffer{command_buffer}); var buffer_map_complete = false; - _ = staging_buffer.mapAsync(wgpu.MapModes.read, 0, output_size, wgpu.BufferMapCallbackInfo { + _ = staging_buffer.mapAsync(wgpu.MapMode{ .read = true }, 0, output_size, wgpu.BufferMapCallbackInfo { .callback = handleBufferMap, .userdata1 = @ptrCast(&buffer_map_complete), }); diff --git a/src/adapter.zig b/src/adapter.zig index b319239..03ca0f6 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -20,10 +20,10 @@ const Instance = @import("instance.zig").Instance; const _device = @import("device.zig"); const Device = _device.Device; const DeviceDescriptor = _device.DeviceDescriptor; -const RequestDeviceCallback = _device.RequestDeviceCallback; +const WGPUDeviceDescriptor = _device.WGPUDeviceDescriptor; +const WGPUDeviceExtras = _device.WGPUDeviceExtras; const RequestDeviceCallbackInfo = _device.RequestDeviceCallbackInfo; -const RequestDeviceStatus = _device.RequestDeviceStatus; -const RequestDeviceResponse = _device.RequestDeviceResponse; +const RequestDeviceError = _device.RequestDeviceError; const _async = @import("async.zig"); const CallbackMode = _async.CallbackMode; @@ -71,6 +71,42 @@ pub const RequestAdapterOptions = extern struct { power_preference: PowerPreference = PowerPreference.@"undefined", + // If true, requires the adapter to be a "fallback" adapter as defined by the JS spec. + // If this is not possible, the request returns null. + force_fallback_adapter: bool = false, + + // If set, requires the adapter to have a particular backend type. + // If this is not possible, the request returns null. + backend_type: BackendType = BackendType.@"undefined", + + // If set, requires the adapter to be able to output to a particular surface. + // If this is not possible, the request returns null. + compatible_surface: ?*Surface = null, + + pub fn toWGPU(self: RequestAdapterOptions) WGPURequestAdapterOptions { + return WGPURequestAdapterOptions{ + .next_in_chain = self.next_in_chain, + .feature_level = self.feature_level, + .power_preference = self.power_preference, + .force_fallback_adapter = @intFromBool(self.force_fallback_adapter), + .backend_type = self.backend_type, + .compatible_surface = self.compatible_surface, + }; + } +}; + +pub const WGPURequestAdapterOptions = extern struct { + next_in_chain: ?*const ChainedStruct = null, + + // "Feature level" for the adapter request. If an adapter is returned, + // it must support the features and limits in the requested feature level. + // + // Implementations may ignore FeatureLevel.compatibility and provide FeatureLevel.core instead. + // FeatureLevel.core is the default in the JS API, but in C, this field is **required** (must not be undefined). + feature_level: FeatureLevel = FeatureLevel.core, + + power_preference: PowerPreference = PowerPreference.@"undefined", + // If true, requires the adapter to be a "fallback" adapter as defined by the JS spec. // If this is not possible, the request returns null. force_fallback_adapter: WGPUBool = @intFromBool(false), @@ -82,9 +118,20 @@ pub const RequestAdapterOptions = extern struct { // If set, requires the adapter to be able to output to a particular surface. // If this is not possible, the request returns null. compatible_surface: ?*Surface = null, + + pub fn toRequestAdapterOptions(self: WGPURequestAdapterOptions) RequestAdapterOptions { + return RequestAdapterOptions{ + .next_in_chain = self.next_in_chain, + .feature_level = self.feature_level, + .power_preference = self.power_preference, + .force_fallback_adapter = self.force_fallback_adapter != 0, + .backend_type = self.backend_type, + .compatible_surface = self.compatible_surface, + }; + } }; -pub const RequestAdapterStatus = enum(u32) { +const RequestAdapterStatus = enum(u32) { success = 0x00000001, instance_dropped = 0x00000002, unavailable = 0x00000003, @@ -92,6 +139,13 @@ pub const RequestAdapterStatus = enum(u32) { unknown = 0x00000005, }; +pub const RequestAdapterError = error { + RequestAdapterInstanceDropped, + RequestAdapterUnavailable, + RequestAdapterError, + RequestAdapterUnknown, +}; + pub const RequestAdapterCallbackInfo = extern struct { next_in_chain: ?*ChainedStruct = null, @@ -101,6 +155,41 @@ pub const RequestAdapterCallbackInfo = extern struct { callback: RequestAdapterCallback, userdata1: ?*anyopaque = null, userdata2: ?*anyopaque = null, + + pub fn init( + mode: ?CallbackMode, + userdata: anytype, + callback: *const fn(RequestAdapterError!*Adapter, message: ?[]const u8, _userdata: @TypeOf(userdata)) void, + ) RequestAdapterCallbackInfo { + const UserDataType = @TypeOf(userdata); + const CallbackType = @TypeOf(callback); + if (@typeInfo(UserDataType) != .pointer) { + @compileError("userdata should be a pointer type"); + } + const Trampoline = struct { + fn cb(status: RequestAdapterStatus, adapter: ?*Adapter, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { + const wrapped_callback: CallbackType = @ptrCast(userdata2); + const _userdata: UserDataType = @ptrCast(@alignCast(userdata1)); + const response: RequestAdapterError!*Adapter = switch (status) { + .success => adapter.?, + .instance_dropped => RequestAdapterError.RequestAdapterInstanceDropped, + .unavailable => RequestAdapterError.RequestAdapterUnavailable, + .@"error" => RequestAdapterError.RequestAdapterError, + .unknown => RequestAdapterError.RequestAdapterUnknown, + }; + wrapped_callback(response, message.toSlice(), _userdata); + } + }; + + return RequestAdapterCallbackInfo { + // TODO: Revisit this default if/when Instance.waitAny() is implemented. + .mode = mode orelse CallbackMode.allow_process_events, + + .callback = Trampoline.cb, + .userdata1 = @ptrCast(userdata), + .userdata2 = @constCast(@ptrCast(callback)), + }; + } }; // TODO: This should maybe be relocated to instance.zig; it is only used there. @@ -112,19 +201,36 @@ pub const RequestAdapterCallback = *const fn( userdata2: ?*anyopaque, ) callconv(.C) void; -pub const RequestAdapterResponse = struct { - status: RequestAdapterStatus, - message: ?[]const u8, - adapter: ?*Adapter, -}; -pub const AdapterInfoProcs = struct { - pub const FreeMembers = *const fn(AdapterInfo) callconv(.C) void; -}; +extern fn wgpuAdapterInfoFreeMembers(adapter_info: WGPUAdapterInfo) void; + +pub const AdapterInfo = struct { + next_in_chain: ?*ChainedStructOut = null, + vendor: ?[]const u8, + architecture: ?[]const u8, + device: ?[]const u8, + description: ?[]const u8, + backend_type: BackendType, + adapter_type: AdapterType, + vendor_id: u32, + device_id: u32, -extern fn wgpuAdapterInfoFreeMembers(adapter_info: AdapterInfo) void; + pub inline fn freeMembers(self: AdapterInfo) void { + wgpuAdapterInfoFreeMembers(WGPUAdapterInfo{ + .next_in_chain = self.next_in_chain, + .vendor = .fromSlice(self.vendor), + .architecture = .fromSlice(self.architecture), + .device = .fromSlice(self.device), + .description = .fromSlice(self.description), + .backend_type = self.backend_type, + .adapter_type = self.adapter_type, + .vendor_id = self.vendor_id, + .device_id = self.device_id, + }); + } +}; -pub const AdapterInfo = extern struct { +pub const WGPUAdapterInfo = extern struct { next_in_chain: ?*ChainedStructOut = null, vendor: StringView, architecture: StringView, @@ -135,82 +241,107 @@ pub const AdapterInfo = extern struct { vendor_id: u32, device_id: u32, - pub inline fn freeMembers(self: AdapterInfo) void { + pub inline fn freeMembers(self: WGPUAdapterInfo) void { wgpuAdapterInfoFreeMembers(self); } }; -pub const AdapterProcs = struct { - pub const GetFeatures = *const fn(*Adapter, *SupportedFeatures) callconv(.C) void; - pub const GetLimits = *const fn(*Adapter, *Limits) callconv(.C) Status; - pub const GetInfo = *const fn(*Adapter, *AdapterInfo) callconv(.C) Status; - pub const HasFeature = *const fn(*Adapter, FeatureName) callconv(.C) WGPUBool; - pub const RequestDevice = *const fn(*Adapter, ?*const DeviceDescriptor, RequestDeviceCallbackInfo) callconv(.C) Future; - pub const AddRef = *const fn(*Adapter) callconv(.C) void; - pub const Release = *const fn(*Adapter) callconv(.C) void; -}; - extern fn wgpuAdapterGetFeatures(adapter: *Adapter, features: *SupportedFeatures) void; extern fn wgpuAdapterGetLimits(adapter: *Adapter, limits: *Limits) Status; -extern fn wgpuAdapterGetInfo(adapter: *Adapter, info: *AdapterInfo) Status; +extern fn wgpuAdapterGetInfo(adapter: *Adapter, info: *WGPUAdapterInfo) Status; extern fn wgpuAdapterHasFeature(adapter: *Adapter, feature: FeatureName) WGPUBool; -extern fn wgpuAdapterRequestDevice(adapter: *Adapter, descriptor: ?*const DeviceDescriptor, callback_info: RequestDeviceCallbackInfo) Future; +extern fn wgpuAdapterRequestDevice(adapter: *Adapter, descriptor: ?*const WGPUDeviceDescriptor, callback_info: RequestDeviceCallbackInfo) Future; extern fn wgpuAdapterAddRef(adapter: *Adapter) void; extern fn wgpuAdapterRelease(adapter: *Adapter) void; +pub const AdapterError = error { + FailedToGetLimits, + FailedToGetInfo, +} || RequestDeviceError || std.mem.Allocator.Error; + pub const Adapter = opaque{ - pub inline fn getFeatures(self: *Adapter, features: *SupportedFeatures) void { - wgpuAdapterGetFeatures(self, features); + pub inline fn getFeatures(self: *Adapter, allocator: std.mem.Allocator) AdapterError![]FeatureName { + var features: SupportedFeatures = undefined; + defer features.freeMembers(); + + wgpuAdapterGetFeatures(self, &features); + + return try allocator.dupe(FeatureName, features.features[0..features.feature_count]); } - pub inline fn getLimits(self: *Adapter, limits: *Limits) Status { - return wgpuAdapterGetLimits(self, limits); + pub inline fn getLimits(self: *Adapter) AdapterError!Limits { + var limits = Limits{}; + if(wgpuAdapterGetLimits(self, &limits) == .@"error") + return error.FailedToGetLimits; + return limits; } - pub inline fn getInfo(self: *Adapter, info: *AdapterInfo) Status { - return wgpuAdapterGetInfo(self, info); + pub inline fn getInfo(self: *Adapter) AdapterError!AdapterInfo { + var adapter_info = WGPUAdapterInfo{}; + if(wgpuAdapterGetInfo(self, &adapter_info) == .@"error") + return error.FailedToGetAdapterInfo; + return AdapterInfo{ + .next_in_chain = adapter_info.next_in_chain, + .vendor = adapter_info.vendor.toSlice(), + .architecture = adapter_info.architecture.toSlice(), + .device = adapter_info.device.toSlice(), + .description = adapter_info.description.toSlice(), + .backend_type = adapter_info.backend_type, + .adapter_type = adapter_info.adapter_type, + .vendor_id = adapter_info.vendor_id, + .device_id = adapter_info.device_id, + }; } pub inline fn hasFeature(self: *Adapter, feature: FeatureName) bool { return wgpuAdapterHasFeature(self, feature) != 0; } - fn defaultDeviceCallback(status: RequestDeviceStatus, device: ?*Device, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { - const ud_response: *RequestDeviceResponse = @ptrCast(@alignCast(userdata1)); - ud_response.* = RequestDeviceResponse { - .status = status, - .message = message.toSlice(), - .device = device, + fn defaultDeviceCallback(response: RequestDeviceError!*Device, maybe_message: ?[]const u8, userdata: *?RequestDeviceError!*Device) void { + userdata.* = response catch blk: { + if (maybe_message) |message| { + std.log.err("{s}\n", .{message}); + } + break :blk response; }; - - const completed: *bool = @ptrCast(@alignCast(userdata2)); - completed.* = true; } // This is a synchronous wrapper that handles asynchronous (callback) logic. // It uses polling to see when the request has been fulfilled, so needs a polling interval parameter. - pub fn requestDeviceSync(self: *Adapter, instance: *Instance, descriptor: ?*const DeviceDescriptor, polling_interval_nanoseconds: u64) RequestDeviceResponse { - var response: RequestDeviceResponse = undefined; - var completed = false; - const callback_info = RequestDeviceCallbackInfo { - .callback = defaultDeviceCallback, - .userdata1 = @ptrCast(&response), - .userdata2 = @ptrCast(&completed), - }; - const device_future = wgpuAdapterRequestDevice(self, descriptor, callback_info); + pub fn requestDeviceSync(self: *Adapter, instance: *Instance, descriptor: ?DeviceDescriptor, polling_interval_nanoseconds: u64) AdapterError!*Device { + var device_response: ?RequestDeviceError!*Device = null; + + const callback_info = RequestDeviceCallbackInfo.init( + null, + &device_response, + defaultDeviceCallback, + ); + const device_future = self.requestDevice( + descriptor, + callback_info, + ); // TODO: Revisit once Instance.waitAny() is implemented in wgpu-native, // it takes in futures and returns when one of them completes. _ = device_future; instance.processEvents(); - while(!completed) { + while (device_response == null) { std.Thread.sleep(polling_interval_nanoseconds); instance.processEvents(); } - return response; + return device_response.?; } - pub inline fn requestDevice(self: *Adapter, descriptor: ?*const DeviceDescriptor, callback_info: RequestDeviceCallbackInfo) Future { - return wgpuAdapterRequestDevice(self, descriptor, callback_info); + pub fn requestDevice( + self: *Adapter, + descriptor: ?DeviceDescriptor, + callback_info: RequestDeviceCallbackInfo, + ) Future { + if(descriptor) |d| { + return wgpuAdapterRequestDevice(self, &d.toWGPU(), callback_info); + } else { + return wgpuAdapterRequestDevice(self, null, callback_info); + } } + pub inline fn addRef(self: *Adapter) void { wgpuAdapterAddRef(self); } @@ -220,18 +351,39 @@ pub const Adapter = opaque{ }; test "can request device" { - const testing = @import("std").testing; - - const instance = Instance.create(null); - const adapter_response = instance.?.requestAdapterSync(null, 200_000_000); - const adapter: ?*Adapter = switch(adapter_response.status) { - .success => adapter_response.adapter, - else => null, - }; - const device_response = adapter.?.requestDeviceSync(instance.?, null, 200_000_000); - const device: ?*Device = switch(device_response.status) { - .success => device_response.device, - else => null + const instance = try Instance.create(null); + const adapter = try instance.requestAdapterSync(null, 0); + const device = try adapter.requestDeviceSync(instance, null, 0); + _ = device; +} + +test "can request device with descriptor" { + const instance = try Instance.create(null); + const adapter = try instance.requestAdapterSync(null, 0); + + const allocator = std.testing.allocator; + const required_features = try adapter.getFeatures(allocator); + defer allocator.free(required_features); + + const limits = try adapter.getLimits(); + + const descriptor = DeviceDescriptor { + .label = "test device", + .required_features = required_features, + .required_limits = limits, + .native_extras = .{ + .trace_path = "./device_trace", + }, + .default_queue = .{ + // TODO: Will need to revisit this after refactoring QueueDescriptor + .label = StringView.fromSlice("test queue"), + }, + + .device_lost_callback_info = .{}, + .uncaptured_error_callback_info = .{}, }; - try testing.expect(device != null); + + const device = try adapter.requestDeviceSync(instance, descriptor, 0); + + _ = device; } \ No newline at end of file diff --git a/src/bind_group.zig b/src/bind_group.zig index e32465f..4ab7755 100644 --- a/src/bind_group.zig +++ b/src/bind_group.zig @@ -67,12 +67,6 @@ pub const BindGroupLayoutDescriptor = extern struct { entries: [*]const BindGroupLayoutEntry, }; -pub const BindGroupLayoutProcs = struct { - pub const SetLabel = *const fn(*BindGroupLayout, StringView) callconv(.C) void; - pub const AddRef = *const fn(*BindGroupLayout) callconv(.C) void; - pub const Release = *const fn(*BindGroupLayout) callconv(.C) void; -}; - extern fn wgpuBindGroupLayoutSetLabel(bind_group_layout: *BindGroupLayout, label: StringView) void; extern fn wgpuBindGroupLayoutAddRef(bind_group_layout: *BindGroupLayout) void; extern fn wgpuBindGroupLayoutRelease(bind_group_layout: *BindGroupLayout) void; @@ -128,12 +122,6 @@ pub const BindGroupDescriptor = extern struct { entries: [*]const BindGroupEntry, }; -pub const BindGroupProcs = struct { - pub const SetLabel = *const fn(*BindGroup, StringView) callconv(.C) void; - pub const AddRef = *const fn(*BindGroup) callconv(.C) void; - pub const Release = *const fn(*BindGroup) callconv(.C) void; -}; - extern fn wgpuBindGroupSetLabel(bind_group: *BindGroup, label: StringView) void; extern fn wgpuBindGroupAddRef(bind_group: *BindGroup) void; extern fn wgpuBindGroupRelease(bind_group: *BindGroup) void; diff --git a/src/buffer.zig b/src/buffer.zig index a09e84a..a51a425 100644 --- a/src/buffer.zig +++ b/src/buffer.zig @@ -28,19 +28,20 @@ pub const BufferBindingLayout = extern struct { min_binding_size: u64 = 0, }; -pub const BufferUsage = WGPUFlags; -pub const BufferUsages = struct { - pub const none = @as(BufferUsage, 0x0000000000000000); - pub const map_read = @as(BufferUsage, 0x0000000000000001); - pub const map_write = @as(BufferUsage, 0x0000000000000002); - pub const copy_src = @as(BufferUsage, 0x0000000000000004); - pub const copy_dst = @as(BufferUsage, 0x0000000000000008); - pub const index = @as(BufferUsage, 0x0000000000000010); - pub const vertex = @as(BufferUsage, 0x0000000000000020); - pub const uniform = @as(BufferUsage, 0x0000000000000040); - pub const storage = @as(BufferUsage, 0x0000000000000080); - pub const indirect = @as(BufferUsage, 0x0000000000000100); - pub const query_resolve = @as(BufferUsage, 0x0000000000000200); +pub const BufferUsage = packed struct(WGPUFlags) { + map_read: bool = false, + map_write: bool = false, + copy_src: bool = false, + copy_dst: bool = false, + index: bool = false, + vertex: bool = false, + uniform: bool = false, + storage: bool = false, + indirect: bool = false, + query_resolve: bool = false, + _: u54 = 0, + + pub const none = BufferUsage{}; }; pub const BufferMapState = enum(u32) { @@ -49,11 +50,12 @@ pub const BufferMapState = enum(u32) { mapped = 0x00000003, }; -pub const MapMode = WGPUFlags; -pub const MapModes = struct { - pub const none = @as(MapMode, 0x0000000000000000); - pub const read = @as(MapMode, 0x0000000000000001); - pub const write = @as(MapMode, 0x0000000000000002); +pub const MapMode = packed struct(WGPUFlags) { + read: bool = false, + write: bool = false, + _: u62 = 0, + + pub const none = MapMode{}; }; pub const MapAsyncStatus = enum(u32) { @@ -85,20 +87,6 @@ pub const BufferDescriptor = extern struct { mapped_at_creation: WGPUBool = @intFromBool(false), }; -pub const BufferProcs = struct { - pub const Destroy = *const fn(*Buffer) callconv(.C) void; - pub const GetConstMappedRange = *const fn(*Buffer, usize, usize) callconv(.C) ?*const anyopaque; - pub const GetMapState = *const fn(*Buffer) callconv(.C) BufferMapState; - pub const GetMappedRange = *const fn(*Buffer, usize, usize) callconv(.C) ?*anyopaque; - pub const GetSize = *const fn(*Buffer) callconv(.C) u64; - pub const GetUsage = *const fn(*Buffer) callconv(.C) BufferUsage; - pub const MapAsync = *const fn(*Buffer, MapMode, usize, usize, BufferMapCallbackInfo) callconv(.C) Future; - pub const SetLabel = *const fn(*Buffer, StringView) callconv(.C) void; - pub const Unmap = *const fn(*Buffer) callconv(.C) void; - pub const AddRef = *const fn(*Buffer) callconv(.C) void; - pub const Release = *const fn(*Buffer) callconv(.C) void; -}; - extern fn wgpuBufferDestroy(buffer: *Buffer) void; extern fn wgpuBufferGetConstMappedRange(buffer: *Buffer, offset: usize, size: usize) ?*const anyopaque; extern fn wgpuBufferGetMapState(buffer: *Buffer) BufferMapState; diff --git a/src/command_encoder.zig b/src/command_encoder.zig index 847a54e..348b2c7 100644 --- a/src/command_encoder.zig +++ b/src/command_encoder.zig @@ -49,26 +49,6 @@ pub const CommandEncoderDescriptor = extern struct { label: StringView = StringView {}, }; -const ComputePassEncoderProcs = struct { - pub const DispatchWorkgroups = *const fn(*ComputePassEncoder, u32, u32, u32) callconv(.C) void; - pub const DispatchWorkgroupsIndirect = *const fn(*ComputePassEncoder, *Buffer, u64) callconv(.C) void; - pub const End = *const fn(*ComputePassEncoder) callconv(.C) void; - pub const InsertDebugMarker = *const fn(*ComputePassEncoder, StringView) callconv(.C) void; - pub const PopDebugGroup = *const fn(*ComputePassEncoder) callconv(.C) void; - pub const PushDebugGroup = *const fn(*ComputePassEncoder, StringView) callconv(.C) void; - pub const SetBindGroup = *const fn(*ComputePassEncoder, u32, *BindGroup, usize, ?[*]const u32) callconv(.C) void; - pub const SetLabel = *const fn(*ComputePassEncoder, StringView) callconv(.C) void; - pub const SetPipeline = *const fn(*ComputePassEncoder, *ComputePipeline) callconv(.C) void; - pub const AddRef = *const fn(*ComputePassEncoder) callconv(.C) void; - pub const Release = *const fn(*ComputePassEncoder) callconv(.C) void; - - // wgpu-native procs? - // pub const SetPushConstants = *const fn(*ComputePassEncoder, u32, u32, *const anyopaque) callconv(.C) void; - // pub const BeginPipelineStatisticsQuery = *const fn(*ComputePassEncoder, *QuerySet, u32) callconv(.C) void; - // pub const EndPipelineStatisticsQuery = *const fn(*ComputePassEncoder) callconv(.C) void; - // pub const WriteTimestamp = *const fn(*ComputePassEncoder, *QuerySet, u32) callconv(.C) void; -}; - extern fn wgpuComputePassEncoderDispatchWorkgroups(compute_pass_encoder: *ComputePassEncoder, workgroup_count_x: u32, workgroup_count_y: u32, workgroup_count_z: u32) void; extern fn wgpuComputePassEncoderDispatchWorkgroupsIndirect(compute_pass_encoder: *ComputePassEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void; extern fn wgpuComputePassEncoderEnd(compute_pass_encoder: *ComputePassEncoder) void; @@ -210,41 +190,6 @@ pub const RenderPassDescriptor = extern struct { } }; -pub const RenderPassEncoderProcs = struct { - pub const BeginOcclusionQuery = *const fn(*RenderPassEncoder, u32) callconv(.C) void; - pub const Draw = *const fn(*RenderPassEncoder, u32, u32, u32, u32) callconv(.C) void; - pub const DrawIndexed = *const fn(*RenderPassEncoder, u32, u32, u32, i32, u32) callconv(.C) void; - pub const DrawIndexedIndirect = *const fn(*RenderPassEncoder, *Buffer, u64) callconv(.C) void; - pub const DrawIndirect = *const fn(*RenderPassEncoder, *Buffer, u64) callconv(.C) void; - pub const End = *const fn(*RenderPassEncoder) callconv(.C) void; - pub const EndOcclusionQuery = *const fn(*RenderPassEncoder) callconv(.C) void; - pub const ExecuteBundles = *const fn(*RenderPassEncoder, usize, [*]const *const RenderBundle) callconv(.C) void; - pub const InsertDebugMarker = *const fn(*RenderPassEncoder, StringView) callconv(.C) void; - pub const PopDebugGroup = *const fn(*RenderPassEncoder) callconv(.C) void; - pub const PushDebugGroup = *const fn(*RenderPassEncoder, StringView) callconv(.C) void; - pub const SetBindGroup = *const fn(*RenderPassEncoder, u32, *BindGroup, usize, ?[*]const u32) callconv(.C) void; - pub const SetBlendConstant = *const fn(*RenderPassEncoder, *const Color) callconv(.C) void; - pub const SetIndexBuffer = *const fn(*RenderPassEncoder, *Buffer, IndexFormat, u64, u64) callconv(.C) void; - pub const SetLabel = *const fn(*RenderPassEncoder, StringView) callconv(.C) void; - pub const SetPipeline = *const fn(*RenderPassEncoder, *RenderPipeline) callconv(.C) void; - pub const SetScissorRect = *const fn(*RenderPassEncoder, u32, u32, u32, u32) callconv(.C) void; - pub const SetStencilReference = *const fn(*RenderPassEncoder, u32) callconv(.C) void; - pub const SetVertexBuffer = *const fn(*RenderPassEncoder, u32, *Buffer, u64, u64) callconv(.C) void; - pub const SetViewport = *const fn(*RenderPassEncoder, f32, f32, f32, f32, f32, f32) callconv(.C) void; - pub const AddRef = *const fn(*RenderPassEncoder) callconv(.C) void; - pub const Release = *const fn(*RenderPassEncoder) callconv(.C) void; - - // wgpu-native procs? - // pub const SetPushConstants = *const fn(*RenderPassEncoder, ShaderStage, u32, u32, *const anyopaque) callconv(.C) void; - // pub const MultiDrawIndirect = *const fn(*RenderPassEncoder, *Buffer, u64, u32) callconv(.C) void; - // pub const MultiDrawIndexedIndirect = *const fn(*RenderPassEncoder, *Buffer, u64, u32) callconv(.C) void; - // pub const MultiDrawIndirectCount = *const fn(*RenderPassEncoder, *Buffer, u64, *Buffer, u64, u32) callconv(.C) void; - // pub const MultiDrawIndexedIndirectCount = *const fn(*RenderPassEncoder, *Buffer, u64, *Buffer, u64, u32) callconv(.C) void; - // pub const BeginPipelineStatisticsQuery = *const fn(*RenderPassEncoder, *QuerySet, u32) callconv(.C) void; - // pub const EndPipelineStatisticsQuery = *const fn(*RenderPassEncoder) callconv(.C) void; - // pub const WriteTimestamp = *const fn(*RenderPassEncoder, *QuerySet, u32) callconv(.C) void; -}; - extern fn wgpuRenderPassEncoderBeginOcclusionQuery(render_pass_encoder: *RenderPassEncoder, query_index: u32) void; extern fn wgpuRenderPassEncoderDraw(render_pass_encoder: *RenderPassEncoder, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void; extern fn wgpuRenderPassEncoderDrawIndexed(render_pass_encoder: *RenderPassEncoder, index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32) void; @@ -382,12 +327,6 @@ pub const CommandBufferDescriptor = extern struct { label: StringView = StringView {}, }; -pub const CommandBufferProcs = struct { - pub const SetLabel = *const fn(*CommandBuffer, StringView) callconv(.C) void; - pub const AddRef = *const fn(*CommandBuffer) callconv(.C) void; - pub const Release = *const fn(*CommandBuffer) callconv(.C) void; -}; - extern fn wgpuCommandBufferSetLabel(command_buffer: *CommandBuffer, label: StringView) void; extern fn wgpuCommandBufferAddRef(command_buffer: *CommandBuffer) void; extern fn wgpuCommandBufferRelease(command_buffer: *CommandBuffer) void; @@ -407,25 +346,6 @@ pub const CommandBuffer = opaque { } }; -pub const CommandEncoderProcs = struct { - pub const BeginComputePass = *const fn(*CommandEncoder, ?*const ComputePassDescriptor) callconv(.C) ?*ComputePassEncoder; - pub const BeginRenderPass = *const fn(*CommandEncoder, *const RenderPassDescriptor) callconv(.C) ?*RenderPassEncoder; - pub const ClearBuffer = *const fn(*CommandEncoder, *Buffer, u64, u64) callconv(.C) void; - pub const CopyBufferToBuffer = *const fn(*CommandEncoder, *Buffer, u64, *Buffer, u64, u64) callconv(.C) void; - pub const CopyBufferToTexture = *const fn(*CommandEncoder, *const TexelCopyBufferInfo, *const TexelCopyTextureInfo, *const Extent3D) callconv(.C) void; - pub const CopyTextureToBuffer = *const fn(*CommandEncoder, *const TexelCopyTextureInfo, *const TexelCopyBufferInfo, *const Extent3D) callconv(.C) void; - pub const CopyTextureToTexture = *const fn(*CommandEncoder, *const TexelCopyTextureInfo, *const TexelCopyTextureInfo, *const Extent3D) callconv(.C) void; - pub const Finish = *const fn(*CommandEncoder, ?*const CommandBufferDescriptor) callconv(.C) ?*CommandBuffer; - pub const InsertDebugMarker = *const fn(*CommandEncoder, StringView) callconv(.C) void; - pub const PopDebugGroup = *const fn(*CommandEncoder) callconv(.C) void; - pub const PushDebugGroup = *const fn(*CommandEncoder, StringView) callconv(.C) void; - pub const ResolveQuerySet = *const fn(*CommandEncoder, *QuerySet, u32, u32, *Buffer, u64) callconv(.C) void; - pub const SetLabel = *const fn(*CommandEncoder, StringView) callconv(.C) void; - pub const WriteTimestamp = *const fn(*CommandEncoder, *QuerySet, u32) callconv(.C) void; - pub const AddRef = *const fn(*CommandEncoder) callconv(.C) void; - pub const Release = *const fn(*CommandEncoder) callconv(.C) void; -}; - extern fn wgpuCommandEncoderBeginComputePass(command_encoder: *CommandEncoder, descriptor: ?*const ComputePassDescriptor) ?*ComputePassEncoder; extern fn wgpuCommandEncoderBeginRenderPass(command_encoder: *CommandEncoder, descriptor: *const RenderPassDescriptor) ?*RenderPassEncoder; extern fn wgpuCommandEncoderClearBuffer(command_encoder: *CommandEncoder, buffer: *Buffer, offset: u64, size: u64) void; diff --git a/src/device.zig b/src/device.zig index 4fccbb5..7d05f01 100644 --- a/src/device.zig +++ b/src/device.zig @@ -18,7 +18,7 @@ const Future = _async.Future; const _limits = @import("limits.zig"); const Limits = _limits.Limits; -const AdapterInfo = @import("adapter.zig").AdapterInfo; +const AdapterInfo = @import("adapter.zig").WGPUAdapterInfo; const _bind_group = @import("bind_group.zig"); const BindGroupDescriptor = _bind_group.BindGroupDescriptor; @@ -77,6 +77,20 @@ pub const DeviceLostReason = enum(u32) { failed_creation = 0x00000004, }; +// `device` is a reference to the device which was lost. If, and only if, the `reason` is DeviceLostReason.failed_creation, `device` is a non-null pointer to a null Device. +pub const DeviceLostCallback = *const fn(device: *const ?*Device, reason: DeviceLostReason, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void; +pub fn defaultDeviceLostCallback(device: *const ?*Device, reason: DeviceLostReason, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { + _ = device; + _ = userdata1; + _ = userdata2; + + // Without a device you can't really do much of anything, so do a panic here by default. + // For better error handling, implement DeviceLostCallback with your own error handling logic. + // Remember you can pass pointers in through the userdata fields of the DeviceLostCallbackInfo struct; + // you could pass in a simple pointer to a bool or something more complex like a struct. + std.debug.panic("Device lost: reason={s} message=\"{s}\"\n", .{ @tagName(reason), message.toSlice() orelse "" }); +} + pub const DeviceLostCallbackInfo = extern struct { next_in_chain: ?*ChainedStruct = null, @@ -89,23 +103,55 @@ pub const DeviceLostCallbackInfo = extern struct { callback: DeviceLostCallback = defaultDeviceLostCallback, userdata1: ?*anyopaque = null, userdata2: ?*anyopaque = null, + + pub fn init( + userdata: anytype, + callback: *const fn(device: *const ?*Device, reason: DeviceLostReason, message: ?[]const u8, _userdata: @TypeOf(userdata)) void, + ) DeviceLostCallbackInfo { + const UserDataType = @TypeOf(userdata); + const CallbackType = @TypeOf(callback); + if (@typeInfo(UserDataType) != .pointer) { + @compileError("userdata should be a pointer type"); + } + const Trampoline = struct { + fn cb(device: *const ?*Device, reason: DeviceLostReason, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { + const wrapped_callback: CallbackType = @ptrCast(userdata2); + const _userdata: UserDataType = @ptrCast(@alignCast(userdata1)); + wrapped_callback(device, reason, message.toSlice(), _userdata); + } + }; + + return DeviceLostCallbackInfo { + .callback = Trampoline.cb, + .userdata1 = @ptrCast(userdata), + .userdata2 = @constCast(@ptrCast(callback)), + }; + } }; -// `device` is a reference to the device which was lost. If, and only if, the `reason` is DeviceLostReason.failed_creation, `device` is a non-null pointer to a null Device. -pub const DeviceLostCallback = *const fn(device: *const ?*Device, reason: DeviceLostReason, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void; -pub fn defaultDeviceLostCallback(device: *const ?*Device, reason: DeviceLostReason, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { - _ = device; - _ = userdata1; - _ = userdata2; - // Without a device you can't really do much of anything, so do a panic here by default. - // For better error handling, implement DeviceLostCallback with your own error handling logic. - // Remember you can pass pointers in through the userdata fields of the DeviceLostCallbackInfo struct; - // you could pass in a simple pointer to a bool or something more complex like a struct. - std.debug.panic("Device lost: reason={s} message=\"{s}\"\n", .{ @tagName(reason), message.toSlice() orelse "" }); +test "DeviceLostCallbackInfo.init() constructs valid DeviceLostCallbackInfo struct with custom callback" { + const CBStruct = struct { + fn cb(device: *const ?*Device, reason: DeviceLostReason, message: ?[]const u8, userdata: *bool) void { + userdata.* = true; + _ = device; + _ = reason; + _ = message; + } + }; + + var callback_called = false; + const not_device: ?*Device = null; + const cb_info = DeviceLostCallbackInfo.init(&callback_called, CBStruct.cb); + cb_info.callback(¬_device, .unknown, StringView.fromSlice(""), cb_info.userdata1, cb_info.userdata2); + try std.testing.expect(callback_called); } -pub const DeviceExtras = extern struct { +pub const DeviceExtras = struct { + trace_path: []const u8, +}; + +pub const WGPUDeviceExtras = extern struct { chain: ChainedStruct = ChainedStruct { .s_type = SType.device_extras, }, @@ -121,6 +167,13 @@ pub const ErrorType = enum(u32) { }; pub const UncapturedErrorCallback = *const fn(device: ?*Device, error_type: ErrorType, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void; +pub fn defaultUncapturedErrorCallback(device: ?*Device, error_type: ErrorType, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { + _ = device; + _ = userdata1; + _ = userdata2; + + std.log.err("Uncaptured error: reason={s} message=\"{s}\"\n", .{ @tagName(error_type), message.toSlice() orelse "" }); +} pub const ErrorFilter = enum(u32) { validation = 0x00000001, @@ -130,12 +183,90 @@ pub const ErrorFilter = enum(u32) { pub const UncapturedErrorCallbackInfo = extern struct { next_in_chain: ?*const ChainedStruct = null, - callback: ?UncapturedErrorCallback = null, + callback: UncapturedErrorCallback = defaultUncapturedErrorCallback, userdata1: ?*anyopaque = null, userdata2: ?*anyopaque = null, + + pub fn init( + userdata: anytype, + callback: *const fn(device: ?*Device, error_type: ErrorType, message: ?[]const u8, _userdata: @TypeOf(userdata)) void, + ) UncapturedErrorCallbackInfo { + const UserDataType = @TypeOf(userdata); + const CallbackType = @TypeOf(callback); + if (@typeInfo(UserDataType) != .pointer) { + @compileError("userdata should be a pointer type"); + } + const Trampoline = struct { + fn cb(device: ?*Device, error_type: ErrorType, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { + const wrapped_callback: CallbackType = @ptrCast(userdata2); + const _userdata: UserDataType = @ptrCast(@alignCast(userdata1)); + wrapped_callback(device, error_type, message.toSlice(), _userdata); + } + }; + + return UncapturedErrorCallbackInfo { + .callback = Trampoline.cb, + .userdata1 = @ptrCast(userdata), + .userdata2 = @constCast(@ptrCast(callback)), + }; + } }; -pub const DeviceDescriptor = extern struct { + +test "UncapturedErrorCallbackInfo.init() constructs valid UncapturedErrorCallbackInfo struct with custom callback" { + const CBStruct = struct { + fn cb(device: ?*Device, error_type: ErrorType, message: ?[]const u8, userdata: *bool) void { + userdata.* = true; + _ = device; + _ = error_type; + _ = message; + } + }; + + var callback_called = false; + const cb_info = UncapturedErrorCallbackInfo.init(&callback_called, CBStruct.cb); + cb_info.callback(null, .unknown, StringView.fromSlice(""), cb_info.userdata1, cb_info.userdata2); + try std.testing.expect(callback_called); +} + +pub const DeviceDescriptor = struct { + label: []const u8 = "", + required_features: []const FeatureName = &[0]FeatureName {}, + required_limits: ?Limits, + default_queue: QueueDescriptor = QueueDescriptor{}, + device_lost_callback_info: DeviceLostCallbackInfo = DeviceLostCallbackInfo {}, + uncaptured_error_callback_info: UncapturedErrorCallbackInfo = UncapturedErrorCallbackInfo{}, + native_extras: ?DeviceExtras = null, + + pub fn toWGPU(self: *const DeviceDescriptor) WGPUDeviceDescriptor { + var device_extras: ?*const ChainedStruct = undefined; + if(self.native_extras) |native_extras| { + device_extras = @ptrCast(&WGPUDeviceExtras { + .trace_path = .fromSlice(native_extras.trace_path), + }); + } else { + device_extras = null; + } + + // Can't do the normal "if(some_struct.optional_value) |value|" format here, + // because "&value" would be the the address of the value on the stack, not the address of the value in the struct, + // and once this function returns the stack address would be a dangling pointer. + const required_limits_ptr = if (self.required_limits != null) &self.required_limits.? else null; + + return WGPUDeviceDescriptor { + .next_in_chain = device_extras, + .label = .fromSlice(self.label), + .required_feature_count = self.required_features.len, + .required_features = self.required_features.ptr, + .required_limits = required_limits_ptr, + .default_queue = self.default_queue, + .device_lost_callback_info = self.device_lost_callback_info, + .uncaptured_error_callback_info = self.uncaptured_error_callback_info, + }; + } +}; + +pub const WGPUDeviceDescriptor = extern struct { next_in_chain: ?*const ChainedStruct = null, label: StringView = StringView {}, required_feature_count: usize = 0, @@ -145,22 +276,38 @@ pub const DeviceDescriptor = extern struct { device_lost_callback_info: DeviceLostCallbackInfo = DeviceLostCallbackInfo {}, uncaptured_error_callback_info: UncapturedErrorCallbackInfo = UncapturedErrorCallbackInfo{}, - pub inline fn withTracePath(self: DeviceDescriptor, trace_path: []const u8) DeviceDescriptor { - var dd = self; - dd.next_in_chain = @ptrCast(&DeviceExtras { - .trace_path = StringView.fromSlice(trace_path), - }); - return dd; + pub fn toDeviceDescriptor(self: WGPUDeviceDescriptor) DeviceDescriptor { + var device_extras: ?*const WGPUDeviceExtras = null; + if(self.next_in_chain) |n| { + device_extras = @ptrCast(n); + } + + return DeviceDescriptor{ + .label = self.label, + .required_feature_count = self.required_feature_count, + .required_features = self.required_features, + .required_limits = self.required_limits, + .default_queue = self.default_queue, + .device_lost_callback_info = self.device_lost_callback_info, + .uncaptured_error_callback_info = self.uncaptured_error_callback_info, + .native_extras = if(device_extras) |d| d.trace_path.toSlice() else null, + }; } }; -pub const RequestDeviceStatus = enum(u32) { +const RequestDeviceStatus = enum(u32) { success = 0x00000001, instance_dropped = 0x00000002, @"error" = 0x00000003, unknown = 0x00000004, }; +pub const RequestDeviceError = error { + RequestDeviceInstanceDropped, + RequestDeviceError, + RequestDeviceUnknown, +}; + // TODO: This probably belongs in adapter.zig pub const RequestDeviceCallback = *const fn( status: RequestDeviceStatus, @@ -170,12 +317,6 @@ pub const RequestDeviceCallback = *const fn( userdata2: ?*anyopaque ) callconv(.C) void; -pub const RequestDeviceResponse = struct { - status: RequestDeviceStatus, - message: ?[]const u8, - device: ?*Device, -}; - pub const RequestDeviceCallbackInfo = extern struct { next_in_chain: ?*ChainedStruct = null, @@ -185,8 +326,43 @@ pub const RequestDeviceCallbackInfo = extern struct { callback: RequestDeviceCallback, userdata1: ?*anyopaque = null, userdata2: ?*anyopaque = null, + + pub fn init( + mode: ?CallbackMode, + userdata: anytype, + callback: *const fn(RequestDeviceError!*Device, message: ?[]const u8, _userdata: @TypeOf(userdata)) void, + ) RequestDeviceCallbackInfo { + const UserDataType = @TypeOf(userdata); + const CallbackType = @TypeOf(callback); + if (@typeInfo(UserDataType) != .pointer) { + @compileError("userdata should be a pointer type"); + } + const Trampoline = struct { + fn cb(status: RequestDeviceStatus, device: ?*Device, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { + const wrapped_callback: CallbackType = @ptrCast(userdata2); + const _userdata: UserDataType = @ptrCast(@alignCast(userdata1)); + const response: RequestDeviceError!*Device = switch (status) { + .success => device.?, + .instance_dropped => RequestDeviceError.RequestDeviceInstanceDropped, + .@"error" => RequestDeviceError.RequestDeviceError, + .unknown => RequestDeviceError.RequestDeviceUnknown, + }; + wrapped_callback(response, message.toSlice(), _userdata); + } + }; + + return RequestDeviceCallbackInfo { + // TODO: Revisit this default if/when Instance.waitAny() is implemented. + .mode = mode orelse CallbackMode.allow_process_events, + + .callback = Trampoline.cb, + .userdata1 = @ptrCast(userdata), + .userdata2 = @constCast(@ptrCast(callback)), + }; + } }; + pub const PopErrorScopeStatus = enum(u32) { success = 0x00000001, // The error scope stack was successfully popped and a result was reported. instance_dropped = 0x00000002, @@ -223,39 +399,6 @@ pub const PopErrorScopeCallbackInfo = extern struct { userdata2: ?*anyopaque = null, }; -pub const DeviceProcs = struct { - pub const CreateBindGroup = *const fn(*Device, *const BindGroupDescriptor) callconv(.C) ?*BindGroup; - pub const CreateBindGroupLayout = *const fn(*Device, *const BindGroupLayoutDescriptor) callconv(.C) ?*BindGroupLayout; - pub const CreateBuffer = *const fn(*Device, *const BufferDescriptor) callconv(.C) ?*Buffer; - pub const CreateCommandEncoder = *const fn(*Device, *const CommandEncoderDescriptor) callconv(.C) ?*CommandEncoder; - pub const CreateComputePipeline = *const fn(*Device, *const ComputePipelineDescriptor) callconv(.C) ?*ComputePipeline; - pub const CreateComputePipelineAsync = *const fn(*Device, *const ComputePipelineDescriptor, CreateComputePipelineAsyncCallbackInfo) callconv(.C) Future; - pub const CreatePipelineLayout = *const fn(*Device, *const PipelineLayoutDescriptor) callconv(.C) ?*PipelineLayout; - pub const CreateQuerySet = *const fn(*Device, *const QuerySetDescriptor) callconv(.C) ?*QuerySet; - pub const CreateRenderBundleEncoder = *const fn(*Device, *const RenderBundleEncoderDescriptor) callconv(.C) ?*RenderBundleEncoder; - pub const CreateRenderPipeline = *const fn(*Device, *const RenderPipelineDescriptor) callconv(.C) ?*RenderPipeline; - pub const CreateRenderPipelineAsync = *const fn(*Device, *const RenderPipelineDescriptor, CreateRenderPipelineAsyncCallbackInfo) callconv(.C) Future; - pub const CreateSampler = *const fn(*Device, *const SamplerDescriptor) callconv(.C) ?*Sampler; - pub const CreateShaderModule = *const fn(*Device, *const ShaderModuleDescriptor) callconv(.C) ?*ShaderModule; - pub const CreateTexture = *const fn(*Device, *const TextureDescriptor) callconv(.C) ?*Texture; - pub const Destroy = *const fn(*Device) callconv(.C) void; - pub const GetAdapterInfo = *const fn(*Device) callconv(.C) AdapterInfo; - pub const GetFeatures = *const fn(*Device, *SupportedFeatures) callconv(.C) void; - pub const GetLimits = *const fn(*Device, *Limits) callconv(.C) Status; - pub const GetLostFuture = *const fn(*Device) callconv(.C) Future; - pub const GetQueue = *const fn(*Device) callconv(.C) ?*Queue; - pub const HasFeature = *const fn(*Device, FeatureName) callconv(.C) WGPUBool; - pub const PopErrorScope = *const fn(*Device, PopErrorScopeCallbackInfo) callconv(.C) Future; - pub const PushErrorScope = *const fn(*Device, ErrorFilter) callconv(.C) void; - pub const SetLabel = *const fn(*Device, StringView) callconv(.C) void; - pub const AddRef = *const fn(*Device) callconv(.C) void; - pub const Release = *const fn(*Device) callconv(.C) void; - - // wgpu-native procs? - // pub const Poll = *const fn(*Device, WGPUBool, ?*const SubmissionIndex) callconv(.C) WGPUBool; - // pub const CreateShaderModuleSpirV = *const fn(*Device, *const ShaderModuleDescriptorSpirV) callconv(.C) ?*ShaderModule; -}; - extern fn wgpuDeviceCreateBindGroup(device: *Device, descriptor: *const BindGroupDescriptor) ?*BindGroup; extern fn wgpuDeviceCreateBindGroupLayout(device: *Device, descriptor: *const BindGroupLayoutDescriptor) ?*BindGroupLayout; extern fn wgpuDeviceCreateBuffer(device: *Device, descriptor: *const BufferDescriptor) ?*Buffer; @@ -287,21 +430,39 @@ extern fn wgpuDeviceRelease(device: *Device) void; extern fn wgpuDevicePoll(device: *Device, wait: WGPUBool, submission_index: ?*const SubmissionIndex) WGPUBool; extern fn wgpuDeviceCreateShaderModuleSpirV(device: *Device, descriptor: *const ShaderModuleDescriptorSpirV) ?*ShaderModule; +pub const DeviceError = error { + FailedToCreateBindGroup, + FailedToCreateBindGroupLayout, + FailedToCreateBuffer, + FailedToCreateCommandEncoder, + FailedToCreateComputePipeline, + FailedToCreatePipelineLayout, + FailedToCreateQuerySet, + FailedToCreateRenderBundleEncoder, + FailedToCreateRenderPipeline, + FailedToCreateSampler, + FailedToCreateShaderModule, + FailedToCreateTexture, + FailedToCreateShaderModuleSpirV, + FailedToGetQueue, + FailedToGetLimits, +} || std.mem.Allocator.Error; + pub const Device = opaque { - pub inline fn createBindGroup(self: *Device, descriptor: *const BindGroupDescriptor) ?*BindGroup { - return wgpuDeviceCreateBindGroup(self, descriptor); + pub inline fn createBindGroup(self: *Device, descriptor: *const BindGroupDescriptor) !*BindGroup { + return wgpuDeviceCreateBindGroup(self, descriptor) orelse DeviceError.FailedToCreateBindGroup; } - pub inline fn createBindGroupLayout(self: *Device, descriptor: *const BindGroupLayoutDescriptor) ?*BindGroupLayout { - return wgpuDeviceCreateBindGroupLayout(self, descriptor); + pub inline fn createBindGroupLayout(self: *Device, descriptor: *const BindGroupLayoutDescriptor) !*BindGroupLayout { + return wgpuDeviceCreateBindGroupLayout(self, descriptor) orelse DeviceError.FailedToCreateBindGroupLayout; } - pub inline fn createBuffer(self: *Device, descriptor: *const BufferDescriptor) ?*Buffer { - return wgpuDeviceCreateBuffer(self, descriptor); + pub inline fn createBuffer(self: *Device, descriptor: *const BufferDescriptor) !*Buffer { + return wgpuDeviceCreateBuffer(self, descriptor) orelse DeviceError.FailedToCreateBuffer; } - pub inline fn createCommandEncoder(self: *Device, descriptor: *const CommandEncoderDescriptor) ?*CommandEncoder { - return wgpuDeviceCreateCommandEncoder(self, descriptor); + pub inline fn createCommandEncoder(self: *Device, descriptor: *const CommandEncoderDescriptor) !*CommandEncoder { + return wgpuDeviceCreateCommandEncoder(self, descriptor) orelse DeviceError.FailedToCreateCommandEncoder; } - pub inline fn createComputePipeline(self: *Device, descriptor: *const ComputePipelineDescriptor) ?*ComputePipeline { - return wgpuDeviceCreateComputePipeline(self, descriptor); + pub inline fn createComputePipeline(self: *Device, descriptor: *const ComputePipelineDescriptor) !*ComputePipeline { + return wgpuDeviceCreateComputePipeline(self, descriptor) orelse DeviceError.FailedToCreateComputePipeline; } // Unimplemented as of wgpu-native v25.0.2.1, @@ -310,17 +471,17 @@ pub const Device = opaque { // return wgpuDeviceCreateComputePipelineAsync(self, descriptor, callback_info); // } - pub inline fn createPipelineLayout(self: *Device, descriptor: *const PipelineLayoutDescriptor) ?*PipelineLayout { - return wgpuDeviceCreatePipelineLayout(self, descriptor); + pub inline fn createPipelineLayout(self: *Device, descriptor: *const PipelineLayoutDescriptor) !*PipelineLayout { + return wgpuDeviceCreatePipelineLayout(self, descriptor) orelse DeviceError.FailedToCreatePipelineLayout; } - pub inline fn createQuerySet(self: *Device, descriptor: *const QuerySetDescriptor) ?*QuerySet { - return wgpuDeviceCreateQuerySet(self, descriptor); + pub inline fn createQuerySet(self: *Device, descriptor: *const QuerySetDescriptor) !*QuerySet { + return wgpuDeviceCreateQuerySet(self, descriptor) orelse DeviceError.FailedToCreateQuerySet; } - pub inline fn createRenderBundleEncoder(self: *Device, descriptor: *const RenderBundleEncoderDescriptor) ?*RenderBundleEncoder { - return wgpuDeviceCreateRenderBundleEncoder(self, descriptor); + pub inline fn createRenderBundleEncoder(self: *Device, descriptor: *const RenderBundleEncoderDescriptor) !*RenderBundleEncoder { + return wgpuDeviceCreateRenderBundleEncoder(self, descriptor) orelse DeviceError.FailedToCreateRenderBundleEncoder; } - pub inline fn createRenderPipeline(self: *Device, descriptor: *const RenderPipelineDescriptor) ?*RenderPipeline { - return wgpuDeviceCreateRenderPipeline(self, descriptor); + pub inline fn createRenderPipeline(self: *Device, descriptor: *const RenderPipelineDescriptor) !*RenderPipeline { + return wgpuDeviceCreateRenderPipeline(self, descriptor) orelse DeviceError.FailedToCreateRenderPipeline; } // Unimplemented as of wgpu-native v25.0.2.1, @@ -329,14 +490,14 @@ pub const Device = opaque { // return wgpuDeviceCreateRenderPipelineAsync(self, descriptor, callback_info); // } - pub inline fn createSampler(self: *Device, descriptor: *const SamplerDescriptor) ?*Sampler { - return wgpuDeviceCreateSampler(self, descriptor); + pub inline fn createSampler(self: *Device, descriptor: *const SamplerDescriptor) !*Sampler { + return wgpuDeviceCreateSampler(self, descriptor) orelse DeviceError.FailedToCreateSampler; } - pub inline fn createShaderModule(self: *Device, descriptor: *const ShaderModuleDescriptor) ?*ShaderModule { - return wgpuDeviceCreateShaderModule(self, descriptor); + pub inline fn createShaderModule(self: *Device, descriptor: *const ShaderModuleDescriptor) !*ShaderModule { + return wgpuDeviceCreateShaderModule(self, descriptor) orelse DeviceError.FailedToCreateShaderModule; } - pub inline fn createTexture(self: *Device, descriptor: *const TextureDescriptor) ?*Texture { - return wgpuDeviceCreateTexture(self, descriptor); + pub inline fn createTexture(self: *Device, descriptor: *const TextureDescriptor) !*Texture { + return wgpuDeviceCreateTexture(self, descriptor) orelse DeviceError.FailedToCreateTexture; } pub inline fn destroy(self: *Device) void { wgpuDeviceDestroy(self); @@ -348,11 +509,21 @@ pub const Device = opaque { // return wgpuDeviceGetAdapterInfo(self); // } - pub inline fn getFeatures(self: *Device, features: *SupportedFeatures) void { - wgpuDeviceGetFeatures(self, features); + pub inline fn getFeatures(self: *Device, allocator: std.mem.Allocator) DeviceError![]FeatureName { + var features = SupportedFeatures{}; + defer features.freeMembers(); + + wgpuDeviceGetFeatures(self, &features); + + return try allocator.dupe(FeatureName, features.features[0..features.feature_count]); } - pub inline fn getLimits(self: *Device, limits: *Limits) Status { - return wgpuDeviceGetLimits(self, limits); + pub inline fn getLimits(self: *Device) DeviceError!Limits { + const limits = Limits{}; + + if(wgpuDeviceGetLimits(self, &limits) == .@"error") + return DeviceError.FailedToGetLimits; + + return limits; } // Unimplemented as of wgpu-native v25.0.2.1, @@ -362,11 +533,11 @@ pub const Device = opaque { // return wgpuDeviceGetLostFuture(self); // } - pub inline fn getQueue(self: *Device) ?*Queue { - return wgpuDeviceGetQueue(self); + pub inline fn getQueue(self: *Device) !*Queue { + return wgpuDeviceGetQueue(self) orelse DeviceError.FailedToGetQueue; } - pub inline fn hasFeature(self: *Device, feature: FeatureName) WGPUBool { - return wgpuDeviceHasFeature(self, feature); + pub inline fn hasFeature(self: *Device, feature: FeatureName) bool { + return wgpuDeviceHasFeature(self, feature) != 0; } pub inline fn popErrorScope(self: *Device, callback_info: PopErrorScopeCallbackInfo) Future { @@ -393,8 +564,8 @@ pub const Device = opaque { pub inline fn poll(self: *Device, wait: bool, submission_index: ?*const SubmissionIndex) bool { return wgpuDevicePoll(self, @intFromBool(wait), submission_index) != 0; } - pub inline fn createShaderModuleSpirV(self: *Device, descriptor: *const ShaderModuleDescriptorSpirV) ?*ShaderModule { - return wgpuDeviceCreateShaderModuleSpirV(self, descriptor); + pub inline fn createShaderModuleSpirV(self: *Device, descriptor: *const ShaderModuleDescriptorSpirV) !*ShaderModule { + return wgpuDeviceCreateShaderModuleSpirV(self, descriptor) orelse DeviceError.FailedToCreateShaderModuleSpirV; } }; diff --git a/src/global.zig b/src/global.zig deleted file mode 100644 index 7517723..0000000 --- a/src/global.zig +++ /dev/null @@ -1,18 +0,0 @@ -// const StringView = @import("misc.zig").StringView; - -// Generic function return type for wgpuGetProcAddress -// pub const Proc = *const fn() callconv(.C) void; - -// Supposedly getProcAddress is a global function, but it doesn't seem like it should work without being tied to a Device? -// Could be it's one of those functions that's meant to be called with null the first time, TODO: look into that. -// -// Regardless, apparently the reason it exists is because different devices have different drivers and therefore different procs, -// so you need to get the version of the proc that is meant for that particular device. -// -// Although this function appears in webgpu.h, it is currently unimplemented in wgpu-native, -// (https://github.com/gfx-rs/wgpu-native/blob/trunk/src/unimplemented.rs) -// so I'm leaving it here in case it gets implemented eventually, but commented out until/unless that happens. -// extern fn wgpuGetProcAddress(proc_name: StringView) ?Proc; -// pub inline fn getProcAddress(proc_name: StringView) ?Proc { -// return wgpuGetProcAddress(proc_name); -// } \ No newline at end of file diff --git a/src/instance.zig b/src/instance.zig index 2ec163e..af34ce3 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -8,10 +8,10 @@ const SType = _chained_struct.SType; const _adapter = @import("adapter.zig"); const Adapter = _adapter.Adapter; const RequestAdapterOptions = _adapter.RequestAdapterOptions; +const WGPURequestAdapterOptions = _adapter.WGPURequestAdapterOptions; const RequestAdapterCallbackInfo = _adapter.RequestAdapterCallbackInfo; const RequestAdapterCallback = _adapter.RequestAdapterCallback; -const RequestAdapterStatus = _adapter.RequestAdapterStatus; -const RequestAdapterResponse = _adapter.RequestAdapterResponse; +const RequestAdapterError = _adapter.RequestAdapterError; const BackendType = _adapter.BackendType; const _surface = @import("surface.zig"); @@ -28,26 +28,29 @@ const _async = @import("async.zig"); const Future = _async.Future; const WaitStatus = _async.WaitStatus; const FutureWaitInfo = _async.FutureWaitInfo; - -pub const InstanceBackend = WGPUFlags; -pub const InstanceBackends = struct { - pub const all = @as(InstanceBackend, 0x00000000); - pub const vulkan = @as(InstanceBackend, 0x00000001); - pub const gl = @as(InstanceBackend, 0x00000002); - pub const metal = @as(InstanceBackend, 0x00000004); - pub const dx12 = @as(InstanceBackend, 0x00000008); - pub const dx11 = @as(InstanceBackend, 0x00000010); - pub const browser_webgpu = @as(InstanceBackend, 0x00000020); - pub const primary = vulkan | metal | dx12 | browser_webgpu; - pub const secondary = gl | dx11; +const CallbackMode = _async.CallbackMode; + +pub const InstanceBackend = packed struct(WGPUFlags) { + vulkan: bool = false, + gl: bool = false, + metal: bool = false, + dx12: bool = false, + dx11: bool = false, + browser_webgpu: bool = false, + _: u58 = 0, + + pub const all = Instance{}; + pub const primary = InstanceBackend{ .vulkan = true , .metal = true, .dx12 = true, .browser_webgpu = true }; + pub const secondary = InstanceBackend{ .gl = true, .dx11 = true }; }; -pub const InstanceFlag = WGPUFlags; -pub const InstanceFlags = struct { - pub const default = @as(InstanceFlag, 0x00000000); - pub const debug = @as(InstanceFlag, 0x00000001); - pub const validation = @as(InstanceFlag, 0x00000002); - pub const discard_hal_labels = @as(InstanceFlag, 0x00000004); +pub const InstanceFlag = packed struct(WGPUFlags) { + debug: bool = false, + validation: bool = false, + discard_hal_labels: bool = false, + _: u61 = 0, + + pub const default = InstanceFlag{}; }; pub const Dx12Compiler = enum(u32) { @@ -79,7 +82,31 @@ pub const GLFenceBehaviour = enum(u32) { gl_fence_behaviour_auto_finish = 0x00000001, }; -pub const InstanceExtras = extern struct { +pub const InstanceExtras = struct { + backends: InstanceBackend, + flags: InstanceFlag, + dx12_shader_compiler: Dx12Compiler, + gles3_minor_version: Gles3MinorVersion, + gl_fence_behavior: GLFenceBehaviour, + dxil_path: []const u8 = "", + dxc_path: []const u8 = "", + dxc_max_shader_model: DxcMaxShaderModel, + + fn toWGPU(self: InstanceExtras) WGPUInstanceExtras { + return WGPUInstanceExtras { + .backends = self.backends, + .flags = self.flags, + .dx12_shader_compiler = self.dx12_shader_compiler, + .gles3_minor_version = self.gles3_minor_version, + .gl_fence_behavior = self.gl_fence_behavior, + .dxil_path = StringView.fromSlice(self.dxil_path), + .dxc_path = StringView.fromSlice(self.dxc_path), + .dxc_max_shader_model = self.dxc_max_shader_model, + }; + } +}; + +const WGPUInstanceExtras = extern struct { chain: ChainedStruct = ChainedStruct { .s_type = SType.instance_extras, }, @@ -93,7 +120,25 @@ pub const InstanceExtras = extern struct { dxc_max_shader_model: DxcMaxShaderModel, }; -pub const InstanceCapabilities = extern struct { +pub const InstanceCapabilities = struct { + // This struct chain is used as mutable in some places and immutable in others. + next_in_chain: ?*ChainedStructOut = null, + + // Enable use of Instance.waitAny() with `timeoutNS > 0`. + timed_wait_any_enable: bool, + + // The maximum number FutureWaitInfo supported in a call to Instance.waitAny() with `timeoutNS > 0`. + timed_wait_any_max_count: usize, + + fn toWGPU(self: InstanceCapabilities) WGPUInstanceCapabilities { + return WGPUInstanceCapabilities { + .timed_wait_any_enable = @intFromBool(self.timed_wait_any_enable), + .timed_wait_any_max_count = self.timed_wait_any_max_count, + }; + } +}; + +const WGPUInstanceCapabilities = extern struct { // This struct chain is used as mutable in some places and immutable in others. next_in_chain: ?*ChainedStructOut = null, @@ -102,21 +147,43 @@ pub const InstanceCapabilities = extern struct { // The maximum number FutureWaitInfo supported in a call to ::wgpuInstanceWaitAny with `timeoutNS > 0`. timed_wait_any_max_count: usize, -}; -pub const InstanceDescriptor = extern struct { - next_in_chain: ?*const ChainedStruct = null, + fn toInstanceCapabilities(self: WGPUInstanceCapabilities) InstanceCapabilities { + return InstanceCapabilities { + .next_in_chain = self.next_in_chain, + .timed_wait_any_enable = self.timed_wait_any_enable != 0, + .timed_wait_any_max_count = self.timed_wait_any_max_count, + }; + } +}; +pub const InstanceDescriptor = struct { // Instance features to enable features: InstanceCapabilities, + native_extras: ?InstanceExtras = null, + + fn toWGPU(self: InstanceDescriptor) WGPUInstanceDescriptor { + var instance_extras: ?*const ChainedStruct = undefined; + if (self.native_extras) |native_extras| { + instance_extras = @ptrCast(&native_extras.toWGPU()); + } else { + instance_extras = null; + } - pub inline fn withNativeExtras(self: InstanceDescriptor, extras: *InstanceExtras) InstanceDescriptor { - var id = self; - id.next_in_chain = @ptrCast(extras); - return id; + return WGPUInstanceDescriptor { + .next_in_chain = instance_extras, + .features = self.features.toWGPU(), + }; } }; +const WGPUInstanceDescriptor = extern struct { + next_in_chain: ?*const ChainedStruct = null, + + // Instance features to enable + features: WGPUInstanceCapabilities, +}; + pub const WGSLLanguageFeatureName = enum(u32) { readonly_and_readwrite_storage_textures = 0x00000001, packed4x8_integer_dot_product = 0x00000002, @@ -124,13 +191,13 @@ pub const WGSLLanguageFeatureName = enum(u32) { pointer_composite_access = 0x00000004, }; -pub const SupportedWGSLLanguageFeaturesProcs = struct { - pub const FreeMembers = *const fn(SupportedWGSLLanguageFeatures) callconv(.C) void; -}; - extern fn wgpuSupportedWGSLLanguageFeaturesFreeMembers(supported_wgsl_language_features: SupportedWGSLLanguageFeatures) void; -pub const SupportedWGSLLanguageFeatures = extern struct { +pub const SupportedWGSLLanguageFeatures = struct { + features: []const WGSLLanguageFeatureName, +}; + +const WGPUSupportedWGSLLanguageFeatures = extern struct { feature_count: usize, features: [*]const WGSLLanguageFeatureName, @@ -141,32 +208,14 @@ pub const SupportedWGSLLanguageFeatures = extern struct { // } }; -pub const InstanceProcs = struct { - pub const CreateInstance = *const fn(?*const InstanceDescriptor) callconv(.C) ?*Instance; - pub const GetCapabilities = *const fn(*InstanceCapabilities) callconv(.C) Status; - - pub const CreateSurface = *const fn(*Instance, *const SurfaceDescriptor) callconv(.C) ?*Surface; - pub const GetWGSLLanguageFeatures = *const fn(*Instance, *SupportedWGSLLanguageFeatures) callconv(.C) Status; - pub const HasWGSLLanguageFeature = *const fn(*Instance, WGSLLanguageFeatureName) callconv(.C) WGPUBool; - pub const ProcessEvents = *const fn(*Instance) callconv(.C) void; - pub const RequestAdapter = *const fn(*Instance, ?*const RequestAdapterOptions, RequestAdapterCallbackInfo) callconv(.C) Future; - pub const WaitAny = *const fn(*Instance, usize, ?[*] FutureWaitInfo, u64) callconv(.C) WaitStatus; - pub const InstanceAddRef = *const fn(*Instance) callconv(.C) void; - pub const InstanceRelease = *const fn(*Instance) callconv(.C) void; - - // wgpu-native procs? - // pub const GenerateReport = *const fn(*Instance, *GlobalReport) callconv(.C) void; - // pub const EnumerateAdapters = *const fn(*Instance, ?*const EnumerateAdapterOptions, ?[*]Adapter) callconv(.C) usize; -}; - -extern fn wgpuGetInstanceCapabilities(capabilities: *InstanceCapabilities) Status; +extern fn wgpuGetInstanceCapabilities(capabilities: *WGPUInstanceCapabilities) Status; -extern fn wgpuCreateInstance(descriptor: ?*const InstanceDescriptor) ?*Instance; +extern fn wgpuCreateInstance(descriptor: ?*const WGPUInstanceDescriptor) ?*Instance; extern fn wgpuInstanceCreateSurface(instance: *Instance, descriptor: *const SurfaceDescriptor) ?*Surface; -extern fn wgpuInstanceGetWGSLLanguageFeatures(instance: *Instance, features: *SupportedWGSLLanguageFeatures) Status; +extern fn wgpuInstanceGetWGSLLanguageFeatures(instance: *Instance, features: *WGPUSupportedWGSLLanguageFeatures) Status; extern fn wgpuInstanceHasWGSLLanguageFeature(instance: *Instance, feature: WGSLLanguageFeatureName) WGPUBool; extern fn wgpuInstanceProcessEvents(instance: *Instance) void; -extern fn wgpuInstanceRequestAdapter(instance: *Instance, options: ?*const RequestAdapterOptions, callback_info: RequestAdapterCallbackInfo) Future; +extern fn wgpuInstanceRequestAdapter(instance: *Instance, options: ?*const WGPURequestAdapterOptions, callback_info: RequestAdapterCallbackInfo) Future; extern fn wgpuInstanceWaitAny(instance: *Instance, future_count: usize, futures: ?[*] FutureWaitInfo, timeout_ns: u64) WaitStatus; extern fn wgpuInstanceAddRef(instance: *Instance) void; extern fn wgpuInstanceRelease(instance: *Instance) void; @@ -212,16 +261,33 @@ pub const EnumerateAdapterOptions = extern struct { extern fn wgpuGenerateReport(instance: *Instance, report: *GlobalReport) void; extern fn wgpuInstanceEnumerateAdapters(instance: *Instance, options: ?*EnumerateAdapterOptions, adapters: ?[*]*Adapter) usize; +pub const InstanceError = error { + FailedToCreateInstance, + FailedToGetCapabilities, +} || RequestAdapterError || std.mem.Allocator.Error; + pub const Instance = opaque { // This is a global function, but it creates an instance so I put it here. - pub inline fn create(descriptor: ?*const InstanceDescriptor) ?*Instance { - return wgpuCreateInstance(descriptor); + pub fn create(descriptor: ?InstanceDescriptor) InstanceError!*Instance { + var maybe_instance: ?*Instance = undefined; + if (descriptor) |d| { + maybe_instance = wgpuCreateInstance(&d.toWGPU()); + } else { + maybe_instance = wgpuCreateInstance(null); + } + + return maybe_instance orelse InstanceError.FailedToCreateInstance; } // This is also a global function, but I think it would make sense being a member of Instance; - // You'd use it like `const status = Instance.getCapabilities(&capabilities);` - pub inline fn getCapabilities(capabilities: *InstanceCapabilities) Status { - return wgpuGetInstanceCapabilities(capabilities); + // You'd use it like `const capabilities = try Instance.getCapabilities();` + pub inline fn getCapabilities() InstanceError!InstanceCapabilities { + var wgpu_capabilities: WGPUInstanceCapabilities = undefined; + if (wgpuGetInstanceCapabilities(&wgpu_capabilities) == Status.success) { + return wgpu_capabilities.toInstanceCapabilities(); + } else { + return InstanceError.FailedToGetCapabilities; + } } pub inline fn createSurface(self: *Instance, descriptor: *const SurfaceDescriptor) ?*Surface { @@ -245,44 +311,55 @@ pub const Instance = opaque { wgpuInstanceProcessEvents(self); } - fn defaultAdapterCallback(status: RequestAdapterStatus, adapter: ?*Adapter, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { - const ud_response: *RequestAdapterResponse = @ptrCast(@alignCast(userdata1)); - ud_response.* = RequestAdapterResponse { - .status = status, - .message = message.toSlice(), - .adapter = adapter, + // Meant to be used within requestAdapterSync, though it might be a good default to expose. + fn defaultAdapterCallback(response: RequestAdapterError!*Adapter, maybe_message: ?[]const u8, userdata: *?RequestAdapterError!*Adapter) void { + userdata.* = response catch blk: { + if (maybe_message) |message| { + std.log.err("{s}\n", .{message}); + } + break :blk response; }; - - const completed: *bool = @ptrCast(@alignCast(userdata2)); - completed.* = true; } // This is a synchronous wrapper that handles asynchronous (callback) logic. // It uses polling to see when the request has been fulfilled, so needs a polling interval parameter. - pub fn requestAdapterSync(self: *Instance, options: ?*const RequestAdapterOptions, polling_interval_nanoseconds: u64) RequestAdapterResponse { - var response: RequestAdapterResponse = undefined; - var completed = false; - const callback_info = RequestAdapterCallbackInfo { - .callback = defaultAdapterCallback, - .userdata1 = @ptrCast(&response), - .userdata2 = @ptrCast(&completed), - }; - const adapter_future = wgpuInstanceRequestAdapter(self, options, callback_info); + // A polling interval of 0 is valid, and probably what you'd want in most cases. + pub fn requestAdapterSync(self: *Instance, options: ?RequestAdapterOptions, polling_interval_nanoseconds: u64) InstanceError!*Adapter { + var adapter_response: ?RequestAdapterError!*Adapter = null; + + const callback_info = RequestAdapterCallbackInfo.init( + null, + &adapter_response, + defaultAdapterCallback, + ); + const adapter_future = self.requestAdapter( + options, + callback_info, + ); // TODO: Revisit once Instance.waitAny() is implemented in wgpu-native, // it takes in futures and returns when one of them completes. _ = adapter_future; self.processEvents(); - while (!completed) { + while (adapter_response == null) { std.Thread.sleep(polling_interval_nanoseconds); self.processEvents(); } - return response; + return adapter_response.?; } - pub inline fn requestAdapter(self: *Instance, options: ?*const RequestAdapterOptions, callback_info: RequestAdapterCallbackInfo) Future { - return wgpuInstanceRequestAdapter(self, options, callback_info); + + pub fn requestAdapter( + self: *Instance, + options: ?RequestAdapterOptions, + callback_info: RequestAdapterCallbackInfo, + ) Future { + if (options) |o| { + return wgpuInstanceRequestAdapter(self, &o.toWGPU(), callback_info); + } else { + return wgpuInstanceRequestAdapter(self, null, callback_info); + } } // Unimplemented as of wgpu-native v25.0.2.1, @@ -305,27 +382,34 @@ pub const Instance = opaque { pub inline fn generateReport(self: *Instance, report: *GlobalReport) void { wgpuGenerateReport(self, report); } - pub inline fn enumerateAdapters(self: *Instance, options: ?*EnumerateAdapterOptions, adapters: ?[*]*Adapter) usize { - return wgpuInstanceEnumerateAdapters(self, options, adapters); + + // Allocates memory to store the list of Adapters + pub inline fn enumerateAdapters(self: *Instance, allocator: std.mem.Allocator, options: ?*EnumerateAdapterOptions) InstanceError![]*Adapter { + const count = wgpuInstanceEnumerateAdapters(self, options, null); + const adapters = try allocator.alloc(*Adapter, count); + + // TODO: Should we bother checking the returned count at this point or just trust that it matches what we got in the previous call? + _ = wgpuInstanceEnumerateAdapters(self, options, adapters.ptr); + return adapters; } }; test "can create instance (and release it afterwards)" { - const testing = @import("std").testing; + const instance = try Instance.create(null); + instance.release(); +} - const instance = Instance.create(null); - try testing.expect(instance != null); - instance.?.release(); +test "requestAdapterSync returns adapter" { + const instance = try Instance.create(null); + const response = try instance.requestAdapterSync(null, 0); + _ = response; } -test "can request adapter" { +test "can enumerate adapters" { const testing = @import("std").testing; - const instance = Instance.create(null); - const response = instance.?.requestAdapterSync(null, 200_000_000); - const adapter: ?*Adapter = switch(response.status) { - .success => response.adapter, - else => null, - }; - try testing.expect(adapter != null); + const instance = try Instance.create(null); + const adapters = try instance.enumerateAdapters(testing.allocator, null); + defer testing.allocator.free(adapters); + try testing.expect(adapters.len != 0); } \ No newline at end of file diff --git a/src/misc.zig b/src/misc.zig index 60ba1fb..b2a62a4 100644 --- a/src/misc.zig +++ b/src/misc.zig @@ -77,10 +77,6 @@ pub const FeatureName = enum(u32) { timestamp_query_inside_passes = 0x00030025, }; -pub const SupportedFeaturesProcs = struct { - pub const FreeMembers = *const fn(SupportedFeatures) callconv(.C) void; -}; - extern fn wgpuSupportedFeaturesFreeMembers(supported_features: SupportedFeatures) void; pub const SupportedFeatures = extern struct { diff --git a/src/pipeline.zig b/src/pipeline.zig index 2f244a5..a10472b 100644 --- a/src/pipeline.zig +++ b/src/pipeline.zig @@ -55,12 +55,6 @@ pub const PipelineLayoutDescriptor = extern struct { } }; -pub const PipelineLayoutProcs = struct { - pub const SetLabel = *const fn(*PipelineLayout, StringView) callconv(.C) void; - pub const AddRef = *const fn(*PipelineLayout) callconv(.C) void; - pub const Release = *const fn(*PipelineLayout) callconv(.C) void; -}; - extern fn wgpuPipelineLayoutSetLabel(pipeline_layout: *PipelineLayout, label: StringView) void; extern fn wgpuPipelineLayoutAddRef(pipeline_layout: *PipelineLayout) void; extern fn wgpuPipelineLayoutRelease(pipeline_layout: *PipelineLayout) void; @@ -130,13 +124,6 @@ pub const CreateComputePipelineAsyncCallback = *const fn( userdata2: ?*anyopaque, ) callconv(.C) void; -pub const ComputePipelineProcs = struct { - pub const GetBindGroupLayout = *const fn(*ComputePipeline, u32) callconv(.C) ?*BindGroupLayout; - pub const SetLabel = *const fn(*ComputePipeline, StringView) callconv(.C) void; - pub const AddRef = *const fn(*ComputePipeline) callconv(.C) void; - pub const Release = *const fn(*ComputePipeline) callconv(.C) void; -}; - extern fn wgpuComputePipelineGetBindGroupLayout(compute_pipeline: *ComputePipeline, group_index: u32) ?*BindGroupLayout; extern fn wgpuComputePipelineSetLabel(compute_pipeline: *ComputePipeline, label: StringView) void; extern fn wgpuComputePipelineAddRef(compute_pipeline: *ComputePipeline) void; @@ -381,14 +368,15 @@ pub const BlendState = extern struct { }; }; -pub const ColorWriteMask = WGPUFlags; -pub const ColorWriteMasks = struct { - pub const none = @as(ColorWriteMask, 0x0000000000000000); - pub const red = @as(ColorWriteMask, 0x0000000000000001); - pub const green = @as(ColorWriteMask, 0x0000000000000002); - pub const blue = @as(ColorWriteMask, 0x0000000000000004); - pub const alpha = @as(ColorWriteMask, 0x0000000000000008); - pub const all = none | red | green | blue | alpha; +pub const ColorWriteMask = packed struct(WGPUFlags) { + red: bool = false, + green: bool = false, + blue: bool = false, + alpha: bool = false, + _: u60 = 0, + + pub const none = ColorWriteMask{}; + pub const all = ColorWriteMask{ .red = true, .green = true, .blue = true, .alpha = true }; }; pub const ColorTargetState = extern struct { @@ -400,7 +388,7 @@ pub const ColorTargetState = extern struct { format: TextureFormat, blend: ?*const BlendState = null, - write_mask: ColorWriteMask = ColorWriteMasks.all, + write_mask: ColorWriteMask = ColorWriteMask.all, }; pub const FragmentState = extern struct { @@ -424,13 +412,6 @@ pub const RenderPipelineDescriptor = extern struct { fragment: ?*const FragmentState = null, }; -pub const RenderPipelineProcs = struct { - pub const GetBindGroupLayout = *const fn(*RenderPipeline, u32) callconv(.C) ?*BindGroupLayout; - pub const SetLabel = *const fn(*RenderPipeline, StringView) callconv(.C) void; - pub const AddRef = *const fn(*RenderPipeline) callconv(.C) void; - pub const Release = *const fn(*RenderPipeline) callconv(.C) void; -}; - extern fn wgpuRenderPipelineGetBindGroupLayout(render_pipeline: *RenderPipeline, group_index: u32) ?*BindGroupLayout; extern fn wgpuRenderPipelineSetLabel(render_pipeline: *RenderPipeline, label: StringView) void; extern fn wgpuRenderPipelineAddRef(render_pipeline: *RenderPipeline) void; diff --git a/src/query_set.zig b/src/query_set.zig index c1dff6f..b417616 100644 --- a/src/query_set.zig +++ b/src/query_set.zig @@ -48,15 +48,6 @@ pub const QuerySetDescriptor = extern struct { } }; -pub const QuerySetProcs = struct { - pub const Destroy = *const fn(*QuerySet) callconv(.C) void; - pub const GetCount = *const fn(*QuerySet) callconv(.C) u32; - pub const GetType = *const fn(*QuerySet) callconv(.C) QueryType; - pub const SetLabel = *const fn(*QuerySet, StringView) callconv(.C) void; - pub const AddRef = *const fn(*QuerySet) callconv(.C) void; - pub const Release = *const fn(*QuerySet) callconv(.C) void; -}; - extern fn wgpuQuerySetDestroy(query_set: *QuerySet) void; extern fn wgpuQuerySetGetCount(query_set: *QuerySet) u32; extern fn wgpuQuerySetGetType(query_set: *QuerySet) QueryType; diff --git a/src/queue.zig b/src/queue.zig index b5a5bef..ed95d8a 100644 --- a/src/queue.zig +++ b/src/queue.zig @@ -40,19 +40,6 @@ pub const QueueWorkDoneCallbackInfo = extern struct { pub const QueueWorkDoneCallback = *const fn(status: WorkDoneStatus, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void; -pub const QueueProcs = struct { - pub const OnSubmittedWorkDone = *const fn(*Queue, QueueWorkDoneCallbackInfo) callconv(.C) Future; - pub const SetLabel = *const fn(*Queue, StringView) callconv(.C) void; - pub const Submit = *const fn(*Queue, usize, [*]const *const CommandBuffer) callconv(.C) void; - pub const WriteBuffer = *const fn(*Queue, Buffer, u64, *const anyopaque, usize) callconv(.C) void; - pub const WriteTexture = *const fn(*Queue, *const TexelCopyTextureInfo, *const anyopaque, usize, *const TexelCopyBufferLayout, *const Extent3D) callconv(.C) void; - pub const AddRef = *const fn(*Queue) callconv(.C) void; - pub const Release = *const fn(*Queue) callconv(.C) void; - - // wgpu-native procs? - // pub const SubmitForIndex = *const fn(*Queue, usize, [*]const *const CommandBuffer) callconv(.C) SubmissionIndex; -}; - extern fn wgpuQueueOnSubmittedWorkDone(queue: *Queue, callback_info: QueueWorkDoneCallbackInfo) Future; extern fn wgpuQueueSetLabel(queue: *Queue, label: StringView) void; extern fn wgpuQueueSubmit(queue: *Queue, command_count: usize, commands: [*]const *const CommandBuffer) void; diff --git a/src/render_bundle.zig b/src/render_bundle.zig index c81454a..c03b4cb 100644 --- a/src/render_bundle.zig +++ b/src/render_bundle.zig @@ -21,27 +21,6 @@ pub const RenderBundleEncoderDescriptor = extern struct { stencil_read_only: WGPUBool = @intFromBool(false), }; -pub const RenderBundleEncoderProcs = struct { - pub const Draw = *const fn(*RenderBundleEncoder, u32, u32, u32, u32) callconv(.C) void; - pub const DrawIndexed = *const fn(*RenderBundleEncoder, u32, u32, u32, i32, u32) callconv(.C) void; - pub const DrawIndexedIndirect = *const fn(*RenderBundleEncoder, *Buffer, u64) callconv(.C) void; - pub const DrawIndirect = *const fn(*RenderBundleEncoder, *Buffer, u64) callconv(.C) void; - pub const Finish = *const fn(*RenderBundleEncoder, *const RenderBundleDescriptor) callconv(.C) ?*RenderBundle; - pub const InsertDebugMarker = *const fn(*RenderBundleEncoder, StringView) callconv(.C) void; - pub const PopDebugGroup = *const fn(*RenderBundleEncoder) callconv(.C) void; - pub const PushDebugGroup = *const fn(*RenderBundleEncoder, StringView) callconv(.C) void; - pub const SetBindGroup = *const fn(*RenderBundleEncoder, u32, *BindGroup, usize, ?[*]const u32) callconv(.C) void; - pub const SetIndexBuffer = *const fn(*RenderBundleEncoder, *Buffer, IndexFormat, u64, u64) callconv(.C) void; - pub const SetLabel = *const fn(*RenderBundleEncoder, StringView) callconv(.C) void; - pub const SetPipeline = *const fn(*RenderBundleEncoder, *RenderPipeline) callconv(.C) void; - pub const SetVertexBuffer = *const fn(*RenderBundleEncoder, u32, *Buffer, u64, u64) callconv(.C) void; - pub const AddRef = *const fn(*RenderBundleEncoder) callconv(.C) void; - pub const Release = *const fn(*RenderBundleEncoder) callconv(.C) void; - - // wgpu-native procs? - // pub const SetPushConstants = *const fn(*RenderBundleEncoder, ShaderStage, u32, u32, *const anyopaque) callconv(.C) void; -}; - extern fn wgpuRenderBundleEncoderDraw(render_bundle_encoder: *RenderBundleEncoder, vertex_count: u32, instance_count: u32, first_vertex: u32, first_instance: u32) void; extern fn wgpuRenderBundleEncoderDrawIndexed(render_bundle_encoder: *RenderBundleEncoder, index_count: u32, instance_count: u32, first_index: u32, base_vertex: i32, first_instance: u32) void; extern fn wgpuRenderBundleEncoderDrawIndexedIndirect(render_bundle_encoder: *RenderBundleEncoder, indirect_buffer: *Buffer, indirect_offset: u64) void; @@ -124,12 +103,6 @@ pub const RenderBundleDescriptor = extern struct { label: StringView = StringView {}, }; -pub const RenderBundleProcs = struct { - pub const SetLabel = *const fn(*RenderBundle, StringView) callconv(.C) void; - pub const AddRef = *const fn(*RenderBundle) callconv(.C) void; - pub const Release = *const fn(*RenderBundle) callconv(.C) void; -}; - extern fn wgpuRenderBundleSetLabel(render_bundle: *RenderBundle, label: StringView) void; extern fn wgpuRenderBundleAddRef(render_bundle: *RenderBundle) void; extern fn wgpuRenderBundleRelease(render_bundle: *RenderBundle) void; diff --git a/src/root.zig b/src/root.zig index e20ae4c..000183c 100644 --- a/src/root.zig +++ b/src/root.zig @@ -8,7 +8,6 @@ pub const WGPUFlags = _misc.WGPUFlags; pub const Status = _misc.Status; pub const OptionalBool = _misc.OptionalBool; pub const FeatureName = _misc.FeatureName; -pub const SupportedFeaturesProcs = _misc.SupportedFeaturesProcs; pub const SupportedFeatures = _misc.SupportedFeatures; pub const IndexFormat = _misc.IndexFormat; pub const CompareFunction = _misc.CompareFunction; @@ -22,25 +21,19 @@ pub const AdapterType = _adapter.AdapterType; pub const BackendType = _adapter.BackendType; pub const FeatureLevel = _adapter.FeatureLevel; pub const RequestAdapterOptions = _adapter.RequestAdapterOptions; -pub const RequestAdapterStatus = _adapter.RequestAdapterStatus; pub const RequestAdapterCallbackInfo = _adapter.RequestAdapterCallbackInfo; pub const RequestAdapterCallback = _adapter.RequestAdapterCallback; -pub const RequestAdapterResponse = _adapter.RequestAdapterResponse; -pub const AdapterInfoProcs = _adapter.AdapterInfoProcs; pub const AdapterInfo = _adapter.AdapterInfo; -pub const AdapterProcs = _adapter.AdapterProcs; pub const Adapter = _adapter.Adapter; const _bind_group = @import("bind_group.zig"); pub const BindGroupLayoutEntryExtras = _bind_group.BindGroupLayoutEntryExtras; pub const BindGroupLayoutEntry = _bind_group.BindGroupLayoutEntry; pub const BindGroupLayoutDescriptor = _bind_group.BindGroupLayoutDescriptor; -pub const BindGroupLayoutProcs = _bind_group.BindGroupLayoutProcs; pub const BindGroupLayout = _bind_group.BindGroupLayout; pub const BindGroupEntryExtras = _bind_group.BindGroupEntryExtras; pub const BindGroupEntry = _bind_group.BindGroupEntry; pub const BindGroupDescriptor = _bind_group.BindGroupDescriptor; -pub const BindGroupProcs = _bind_group.BindGroupProcs; pub const BindGroup = _bind_group.BindGroup; const _buffer = @import("buffer.zig"); @@ -48,15 +41,12 @@ pub const WGPU_WHOLE_MAP_SIZE = _buffer.WGPU_WHOLE_MAP_SIZE; pub const BufferBindingType = _buffer.BufferBindingType; pub const BufferBindingLayout = _buffer.BufferBindingLayout; pub const BufferUsage = _buffer.BufferUsage; -pub const BufferUsages = _buffer.BufferUsages; pub const BufferMapState = _buffer.BufferMapState; pub const MapMode = _buffer.MapMode; -pub const MapModes = _buffer.MapModes; pub const MapAsyncStatus = _buffer.MapAsyncStatus; pub const BufferMapCallbackInfo = _buffer.BufferMapCallbackInfo; pub const BufferMapCallback = _buffer.BufferMapCallback; pub const BufferDescriptor = _buffer.BufferDescriptor; -pub const BufferProcs = _buffer.BufferProcs; pub const Buffer = _buffer.Buffer; const _chained_struct = @import("chained_struct.zig"); @@ -80,12 +70,9 @@ pub const DepthStencilAttachment = _command_encoder.DepthStencilAttachment; pub const RenderPassTimestampWrites = _command_encoder.RenderPassTimestampWrites; pub const RenderPassMaxDrawCount = _command_encoder.RenderPassMaxDrawCount; pub const RenderPassDescriptor = _command_encoder.RenderPassDescriptor; -pub const RenderPassEncoderProcs = _command_encoder.RenderPassEncoderProcs; pub const RenderPassEncoder = _command_encoder.RenderPassEncoder; pub const CommandBufferDescriptor = _command_encoder.CommandBufferDescriptor; -pub const CommandBufferProcs = _command_encoder.CommandBufferProcs; pub const CommandBuffer = _command_encoder.CommandBuffer; -pub const CommandEncoderProcs = _command_encoder.CommandEncoderProcs; pub const CommandEncoder = _command_encoder.CommandEncoder; const _device = @import("device.zig"); @@ -99,21 +86,16 @@ pub const UncapturedErrorCallback = _device.UncapturedErrorCallback; pub const ErrorFilter = _device.ErrorFilter; pub const UncapturedErrorCallbackInfo = _device.UncapturedErrorCallbackInfo; pub const DeviceDescriptor = _device.DeviceDescriptor; -pub const RequestDeviceStatus = _device.RequestDeviceStatus; pub const RequestDeviceCallback = _device.RequestDeviceCallback; -pub const RequestDeviceResponse = _device.RequestDeviceResponse; pub const RequestDeviceCallbackInfo = _device.RequestDeviceCallbackInfo; pub const PopErrorScopeStatus = _device.PopErrorScopeStatus; pub const PopErrorScopeCallback = _device.PopErrorScopeCallback; pub const PopErrorScopeCallbackInfo = _device.PopErrorScopeCallbackInfo; -pub const DeviceProcs = _device.DeviceProcs; pub const Device = _device.Device; const _instance = @import("instance.zig"); pub const InstanceBackend = _instance.InstanceBackend; -pub const InstanceBackends = _instance.InstanceBackends; pub const InstanceFlag = _instance.InstanceFlag; -pub const InstanceFlags = _instance.InstanceFlags; pub const Dx12Compiler = _instance.Dx12Compiler; pub const Gles3MinorVersion = _instance.Gles3MinorVersion; pub const DxcMaxShaderModel = _instance.DxcMaxShaderModel; @@ -122,9 +104,7 @@ pub const InstanceExtras = _instance.InstanceExtras; pub const InstanceCapabilities = _instance.InstanceCapabilities; pub const InstanceDescriptor = _instance.InstanceDescriptor; pub const WGSLLanguageFeatureName = _instance.WGSLLanguageFeatureName; -pub const SupportedWGSLLanguageFeaturesProcs = _instance.SupportedWGSLLanguageFeaturesProcs; pub const SupportedWGSLLanguageFeatures = _instance.SupportedWGSLLanguageFeatures; -pub const InstanceProcs = _instance.InstanceProcs; pub const RegistryReport = _instance.RegistryReport; pub const HubReport = _instance.HubReport; pub const GlobalReport = _instance.GlobalReport; @@ -147,7 +127,6 @@ const _pipeline = @import("pipeline.zig"); pub const PushConstantRange = _pipeline.PushConstantRange; pub const PipelineLayoutExtras = _pipeline.PipelineLayoutExtras; pub const PipelineLayoutDescriptor = _pipeline.PipelineLayoutDescriptor; -pub const PipelineLayoutProcs = _pipeline.PipelineLayoutProcs; pub const PipelineLayout = _pipeline.PipelineLayout; pub const ConstantEntry = _pipeline.ConstantEntry; pub const ProgrammableStageDescriptor = _pipeline.ProgrammableStageDescriptor; @@ -155,7 +134,6 @@ pub const ComputePipelineDescriptor = _pipeline.ComputePipelineDescriptor; pub const CreatePipelineAsyncStatus = _pipeline.CreatePipelineAsyncStatus; pub const CreateComputePipelineAsyncCallbackInfo = _pipeline.CreateComputePipelineAsyncCallbackInfo; pub const CreateComputePipelineAsyncCallback = _pipeline.CreateComputePipelineAsyncCallback; -pub const ComputePipelineProcs = _pipeline.ComputePipelineProcs; pub const ComputePipeline = _pipeline.ComputePipeline; pub const VertexStepMode = _pipeline.VertexStepMode; pub const VertexFormat = _pipeline.VertexFormat; @@ -175,11 +153,9 @@ pub const BlendFactor = _pipeline.BlendFactor; pub const BlendComponent = _pipeline.BlendComponent; pub const BlendState = _pipeline.BlendState; pub const ColorWriteMask = _pipeline.ColorWriteMask; -pub const ColorWriteMasks = _pipeline.ColorWriteMasks; pub const ColorTargetState = _pipeline.ColorTargetState; pub const FragmentState = _pipeline.FragmentState; pub const RenderPipelineDescriptor = _pipeline.RenderPipelineDescriptor; -pub const RenderPipelineProcs = _pipeline.RenderPipelineProcs; pub const RenderPipeline = _pipeline.RenderPipeline; pub const CreateRenderPipelineAsyncCallbackInfo = _pipeline.CreateRenderPipelineAsyncCallbackInfo; pub const CreateRenderPipelineAsyncCallback = _pipeline.CreateRenderPipelineAsyncCallback; @@ -189,7 +165,6 @@ pub const QueryType = _query_set.QueryType; pub const PipelineStatisticName = _query_set.PipelineStatisticName; pub const QuerySetDescriptorExtras = _query_set.QuerySetDescriptorExtras; pub const QuerySetDescriptor = _query_set.QuerySetDescriptor; -pub const QuerySetProcs = _query_set.QuerySetProcs; pub const QuerySet = _query_set.QuerySet; const _queue = @import("queue.zig"); @@ -198,15 +173,12 @@ pub const QueueDescriptor = _queue.QueueDescriptor; pub const WorkDoneStatus = _queue.WorkDoneStatus; pub const QueueWorkDoneCallbackInfo = _queue.QueueWorkDoneCallbackInfo; pub const QueueWorkDoneCallback = _queue.QueueWorkDoneCallback; -pub const QueueProcs = _queue.QueueProcs; pub const Queue = _queue.Queue; const _render_bundle = @import("render_bundle.zig"); pub const RenderBundleEncoderDescriptor = _render_bundle.RenderBundleEncoderDescriptor; -pub const RenderBundleEncoderProcs = _render_bundle.RenderBundleEncoderProcs; pub const RenderBundleEncoder = _render_bundle.RenderBundleEncoder; pub const RenderBundleDescriptor = _render_bundle.RenderBundleDescriptor; -pub const RenderBundleProcs = _render_bundle.RenderBundleProcs; pub const RenderBundle = _render_bundle.RenderBundle; const _sampler = @import("sampler.zig"); @@ -216,12 +188,10 @@ pub const AddressMode = _sampler.AddressMode; pub const FilterMode = _sampler.FilterMode; pub const MipmapFilterMode = _sampler.MipmapFilterMode; pub const SamplerDescriptor = _sampler.SamplerDescriptor; -pub const SamplerProcs = _sampler.SamplerProcs; pub const Sampler = _sampler.Sampler; const _shader = @import("shader.zig"); pub const ShaderStage = _shader.ShaderStage; -pub const ShaderStages = _shader.ShaderStages; pub const ShaderModuleDescriptor = _shader.ShaderModuleDescriptor; pub const ShaderModuleDescriptorSpirV = _shader.ShaderModuleDescriptorSpirV; pub const ShaderSourceSPIRV = _shader.ShaderSourceSPIRV; @@ -240,7 +210,6 @@ pub const CompilationMessage = _shader.CompilationMessage; pub const CompilationInfo = _shader.CompilationInfo; pub const CompilationInfoCallback = _shader.CompilationInfoCallback; pub const CompilationInfoCallbackInfo = _shader.CompilationInfoCallbackInfo; -pub const ShaderModuleProcs = _shader.ShaderModuleProcs; pub const ShaderModule = _shader.ShaderModule; const _surface = @import("surface.zig"); @@ -267,11 +236,9 @@ pub const CompositeAlphaMode = _surface.CompositeAlphaMode; pub const PresentMode = _surface.PresentMode; pub const SurfaceConfigurationExtras = _surface.SurfaceConfigurationExtras; pub const SurfaceConfiguration = _surface.SurfaceConfiguration; -pub const SurfaceCapabilitiesProcs = _surface.SurfaceCapabilitiesProcs; pub const SurfaceCapabilities = _surface.SurfaceCapabilities; pub const GetCurrentTextureStatus = _surface.GetCurrentTextureStatus; pub const SurfaceTexture = _surface.SurfaceTexture; -pub const SurfaceProcs = _surface.SurfaceProcs; pub const Surface = _surface.Surface; const _texture = @import("texture.zig"); @@ -280,10 +247,8 @@ pub const WGPU_MIP_LEVEL_COUNT_UNDEFINED = _texture.WGPU_MIP_LEVEL_COUNT_UNDEFIN pub const WGPU_COPY_STRIDE_UNDEFINED = _texture.WGPU_COPY_STRIDE_UNDEFINED; pub const TextureFormat = _texture.TextureFormat; pub const TextureUsage = _texture.TextureUsage; -pub const TextureUsages = _texture.TextureUsages; pub const TextureAspect = _texture.TextureAspect; pub const TextureViewDescriptor = _texture.TextureViewDescriptor; -pub const TextureViewProcs = _texture.TextureViewProcs; pub const TextureView = _texture.TextureView; pub const SampleType = _texture.SampleType; pub const ViewDimension = _texture.ViewDimension; @@ -293,7 +258,6 @@ pub const StorageTextureBindingLayout = _texture.StorageTextureBindingLayout; pub const TextureDimension = _texture.TextureDimension; pub const Extent3D = _texture.Extent3D; pub const TextureDescriptor = _texture.TextureDescriptor; -pub const TextureProcs = _texture.TextureProcs; pub const Texture = _texture.Texture; pub const Origin3D = _texture.Origin3D; pub const TexelCopyTextureInfo = _texture.TexelCopyTextureInfo; @@ -306,3 +270,9 @@ pub const WaitStatus = _async.WaitStatus; pub const Future = _async.Future; pub const FutureWaitInfo = _async.FutureWaitInfo; +test "root" { + // I've read that refAllDecls is considered a bit of a hack that might eventually get removed, + // but right now it's a pretty effective way to run all tests without duplicates, and catch + // syntax errors that were previously not covered by tests. + @import("std").testing.refAllDecls(@This()); +} \ No newline at end of file diff --git a/src/sampler.zig b/src/sampler.zig index 308a03e..3dbc68c 100644 --- a/src/sampler.zig +++ b/src/sampler.zig @@ -55,12 +55,6 @@ pub const SamplerDescriptor = extern struct { max_anisotropy: u16 = 1, }; -pub const SamplerProcs = struct { - pub const SetLabel = *const fn(*Sampler, StringView) callconv(.C) void; - pub const AddRef = *const fn(*Sampler) callconv(.C) void; - pub const Release = *const fn(*Sampler) callconv(.C) void; -}; - extern fn wgpuSamplerSetLabel(sampler: *Sampler, label: StringView) void; extern fn wgpuSamplerAddRef(sampler: *Sampler) void; extern fn wgpuSamplerRelease(sampler: *Sampler) void; diff --git a/src/shader.zig b/src/shader.zig index bc5209e..1d1a216 100644 --- a/src/shader.zig +++ b/src/shader.zig @@ -12,12 +12,13 @@ const _async = @import("async.zig"); const CallbackMode = _async.CallbackMode; const Future = _async.Future; -pub const ShaderStage = WGPUFlags; -pub const ShaderStages = struct { - pub const none = @as(ShaderStage, 0x0000000000000000); - pub const vertex = @as(ShaderStage, 0x0000000000000001); - pub const fragment = @as(ShaderStage, 0x0000000000000002); - pub const compute = @as(ShaderStage, 0x0000000000000004); +pub const ShaderStage = packed struct(WGPUFlags) { + vertex: bool = false, + fragment: bool = false, + compute: bool = false, + _: u61 = 0, + + pub const none = ShaderStage{}; }; pub const ShaderModuleDescriptor = extern struct { @@ -163,13 +164,6 @@ pub const CompilationInfoCallbackInfo = extern struct { userdata2: ?*anyopaque = null, }; -pub const ShaderModuleProcs = struct { - pub const GetCompilationInfo = *const fn(*ShaderModule, CompilationInfoCallbackInfo) callconv(.C) Future; - pub const SetLabel = *const fn(*ShaderModule, StringView) callconv(.C) void; - pub const AddRef = *const fn(*ShaderModule) callconv(.C) void; - pub const Release = *const fn(*ShaderModule) callconv(.C) void; -}; - extern fn wgpuShaderModuleGetCompilationInfo(shader_module: *ShaderModule, callback_info: CompilationInfoCallbackInfo) Future; extern fn wgpuShaderModuleSetLabel(shader_module: *ShaderModule, label: StringView) void; extern fn wgpuShaderModuleAddRef(shader_module: *ShaderModule) void; diff --git a/src/surface.zig b/src/surface.zig index dd99cc2..4d5b77d 100644 --- a/src/surface.zig +++ b/src/surface.zig @@ -10,7 +10,6 @@ const _texture = @import("texture.zig"); const Texture = _texture.Texture; const TextureFormat = _texture.TextureFormat; const TextureUsage = _texture.TextureUsage; -const TextureUsages = _texture.TextureUsages; const _device = @import("device.zig"); const Device = _device.Device; @@ -246,7 +245,7 @@ pub const SurfaceConfiguration = extern struct { format: TextureFormat, // The TextureUsage of the surface's textures. - usage: TextureUsage = TextureUsages.render_attachment, + usage: TextureUsage = TextureUsage{ .render_attachment = true }, // The width of the surface's textures width: u32, @@ -273,10 +272,6 @@ pub const SurfaceConfiguration = extern struct { } }; -pub const SurfaceCapabilitiesProcs = struct { - pub const FreeMembers = *const fn(SurfaceCapabilities) callconv(.C) void; -}; - extern fn wgpuSurfaceCapabilitiesFreeMembers(surface_capabilities: SurfaceCapabilities) void; // Filled by Surface.getCapabilities() with what's supported for Surface.configure() for a pair of Surface and Adapter. @@ -347,17 +342,6 @@ pub const SurfaceTexture = extern struct { status: GetCurrentTextureStatus, }; -pub const SurfaceProcs = struct { - pub const Configure = *const fn(*Surface, *const SurfaceConfiguration) callconv(.C) void; - pub const GetCapabilities = *const fn(*Surface, *Adapter, *SurfaceCapabilities) callconv(.C) Status; - pub const GetCurrentTexture = *const fn(*Surface, *SurfaceTexture) callconv(.C) void; - pub const Present = *const fn(*Surface) callconv(.C) Status; - pub const SetLabel = *const fn(*Surface, StringView) void; - pub const Unconfigure = *const fn(*Surface) callconv(.C) void; - pub const AddRef = *const fn(*Surface) callconv(.C) void; - pub const Release = *const fn(*Surface) callconv(.C) void; -}; - extern fn wgpuSurfaceConfigure(surface: *Surface, config: *const SurfaceConfiguration) void; extern fn wgpuSurfaceGetCapabilities(surface: *Surface, adapter: *Adapter, capabilities: *SurfaceCapabilities) Status; extern fn wgpuSurfaceGetCurrentTexture(surface: *Surface, surface_texture: *SurfaceTexture) void; diff --git a/src/texture.zig b/src/texture.zig index 951d8c3..c596e94 100644 --- a/src/texture.zig +++ b/src/texture.zig @@ -120,14 +120,15 @@ pub const TextureFormat = enum(u32) { nv12 = 0x00030007, }; -pub const TextureUsage = WGPUFlags; -pub const TextureUsages = struct { - pub const none = @as(TextureUsage, 0x0000000000000000); - pub const copy_src = @as(TextureUsage, 0x0000000000000001); - pub const copy_dst = @as(TextureUsage, 0x0000000000000002); - pub const texture_binding = @as(TextureUsage, 0x0000000000000004); - pub const storage_binding = @as(TextureUsage, 0x0000000000000008); - pub const render_attachment = @as(TextureUsage, 0x0000000000000010); +pub const TextureUsage = packed struct(WGPUFlags) { + copy_src: bool = false, + copy_dst: bool = false, + texture_binding: bool = false, + storage_binding: bool = false, + render_attachment: bool = false, + _: u59 = 0, + + pub const none = TextureUsage{}; }; // TODO: Like a lot of things in this file, this breaks from the wrapper code convention by having an unneeded prefix ("Texture") @@ -150,13 +151,7 @@ pub const TextureViewDescriptor = extern struct { base_array_layer: u32 = 0, array_layer_count: u32 = WGPU_ARRAY_LAYER_COUNT_UNDEFINED, aspect: TextureAspect = TextureAspect.all, - usage: TextureUsage = TextureUsages.none, -}; - -pub const TextureViewProcs = struct { - pub const SetLabel = *const fn(*TextureView, StringView) callconv(.C) void; - pub const AddRef = *const fn(*TextureView) callconv(.C) void; - pub const Release = *const fn(*TextureView) callconv(.C) void; + usage: TextureUsage = TextureUsage.none, }; extern fn wgpuTextureViewSetLabel(texture_view: *TextureView, label: StringView) void; @@ -255,22 +250,6 @@ pub const TextureDescriptor = extern struct { view_formats: [*]const TextureFormat = &[_]TextureFormat {}, }; -pub const TextureProcs = struct { - pub const CreateView = *const fn(*Texture, ?*const TextureViewDescriptor) callconv(.C) ?*TextureView; - pub const Destroy = *const fn(*Texture) callconv(.C) void; - pub const GetDepthOrArrayLayers = *const fn(*Texture) callconv(.C) u32; - pub const GetDimension = *const fn(*Texture) callconv(.C) TextureDimension; - pub const GetFormat = *const fn(*Texture) callconv(.C) TextureFormat; - pub const GetHeight = *const fn(*Texture) callconv(.C) u32; - pub const GetMipLevelCount = *const fn(*Texture) callconv(.C) u32; - pub const GetSampleCount = *const fn(*Texture) callconv(.C) u32; - pub const GetUsage = *const fn(*Texture) callconv(.C) TextureUsage; - pub const GetWidth = *const fn(*Texture) callconv(.C) u32; - pub const SetLabel = *const fn(*Texture, StringView) callconv(.C) void; - pub const AddRef = *const fn(*Texture) callconv(.C) void; - pub const Release = *const fn(*Texture) callconv(.C) void; -}; - extern fn wgpuTextureCreateView(texture: *Texture, descriptor: ?*const TextureViewDescriptor) ?*TextureView; extern fn wgpuTextureDestroy(texture: *Texture) void; extern fn wgpuTextureGetDepthOrArrayLayers(texture: *Texture) u32; diff --git a/test-all b/test-all index cb5eb05..8883a6e 100755 --- a/test-all +++ b/test-all @@ -1,2 +1,2 @@ #!/bin/sh -zig build test compute-tests --summary all +zig build unit-tests compute-tests --summary all diff --git a/tests/compute.zig b/tests/compute.zig index b0f9474..336b509 100644 --- a/tests/compute.zig +++ b/tests/compute.zig @@ -14,61 +14,53 @@ fn compute_collatz() ![4]u32 { const numbers_size = @sizeOf(@TypeOf(numbers)); const numbers_length = numbers_size / @sizeOf(u32); - const instance = wgpu.Instance.create(null).?; + const instance = try wgpu.Instance.create(null); defer instance.release(); - const adapter_response = instance.requestAdapterSync(null, 200_000_000); - const adapter = switch(adapter_response.status) { - .success => adapter_response.adapter.?, - else => return error.NoAdapter, - }; + const adapter = try instance.requestAdapterSync(null, 0); defer adapter.release(); - const device_response = adapter.requestDeviceSync(instance, null, 200_000_000); - const device = switch(device_response.status) { - .success => device_response.device.?, - else => return error.NoDevice, - }; + const device = try adapter.requestDeviceSync(instance, null, 0); defer device.release(); - const queue = device.getQueue().?; + const queue = try device.getQueue(); defer queue.release(); - const shader_module = device.createShaderModule(&wgpu.shaderModuleWGSLDescriptor(.{ + const shader_module = try device.createShaderModule(&wgpu.shaderModuleWGSLDescriptor(.{ .label = "compute.wgsl", .code = @embedFile("./compute.wgsl"), - })).?; + })); defer shader_module.release(); - const staging_buffer = device.createBuffer(&wgpu.BufferDescriptor { + const staging_buffer = try device.createBuffer(&wgpu.BufferDescriptor { .label = wgpu.StringView.fromSlice("staging_buffer"), - .usage = wgpu.BufferUsages.map_read | wgpu.BufferUsages.copy_dst, + .usage = wgpu.BufferUsage{ .map_read = true, .copy_dst = true }, .size = numbers_size, .mapped_at_creation = @as(u32, @intFromBool(false)), - }).?; + }); defer staging_buffer.release(); - const storage_buffer = device.createBuffer(&wgpu.BufferDescriptor { + const storage_buffer = try device.createBuffer(&wgpu.BufferDescriptor { .label = wgpu.StringView.fromSlice("storage_buffer"), - .usage = wgpu.BufferUsages.storage | wgpu.BufferUsages.copy_dst | wgpu.BufferUsages.copy_src, + .usage = wgpu.BufferUsage{ .storage = true, .copy_dst = true, .copy_src = true }, .size = numbers_size, .mapped_at_creation = @as(u32, @intFromBool(false)), - }).?; + }); defer storage_buffer.release(); - const compute_pipeline = device.createComputePipeline(&wgpu.ComputePipelineDescriptor { + const compute_pipeline = try device.createComputePipeline(&wgpu.ComputePipelineDescriptor { .label = wgpu.StringView.fromSlice("compute_pipeline"), .compute = wgpu.ProgrammableStageDescriptor { .module = shader_module, .entry_point = wgpu.StringView.fromSlice("main"), }, - }).?; + }); defer compute_pipeline.release(); const bind_group_layout = compute_pipeline.getBindGroupLayout(0).?; defer bind_group_layout.release(); - const bind_group = device.createBindGroup(&wgpu.BindGroupDescriptor { + const bind_group = try device.createBindGroup(&wgpu.BindGroupDescriptor { .label = wgpu.StringView.fromSlice("bind_group"), .layout = bind_group_layout, .entry_count = 1, @@ -80,12 +72,12 @@ fn compute_collatz() ![4]u32 { .size = numbers_size, } }, - }).?; + }); defer bind_group.release(); - const command_encoder = device.createCommandEncoder(&wgpu.CommandEncoderDescriptor { + const command_encoder = try device.createCommandEncoder(&wgpu.CommandEncoderDescriptor { .label = wgpu.StringView.fromSlice("command_encoder"), - }).?; + }); defer command_encoder.release(); const compute_pass_encoder = command_encoder.beginComputePass(&wgpu.ComputePassDescriptor { @@ -111,7 +103,7 @@ fn compute_collatz() ![4]u32 { queue.submit(&[_]*const wgpu.CommandBuffer{command_buffer}); var buffer_map_complete = false; - _ = staging_buffer.mapAsync(wgpu.MapModes.read, 0, numbers_size, wgpu.BufferMapCallbackInfo { + _ = staging_buffer.mapAsync(wgpu.MapMode{ .read = true }, 0, numbers_size, wgpu.BufferMapCallbackInfo { .callback = handleBufferMap, .userdata1 = @ptrCast(&buffer_map_complete), });