From 71c4ebc3027514e8ad65709db7a5bfa9027cb421 Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Mon, 30 Jun 2025 03:42:41 -0500 Subject: [PATCH 01/21] Initial work on making Instance structs/methods more idiomatic --- examples/triangle/triangle.zig | 2 +- src/adapter.zig | 6 +- src/instance.zig | 161 +++++++++++++++++++++++---------- tests/compute.zig | 2 +- 4 files changed, 118 insertions(+), 53 deletions(-) diff --git a/examples/triangle/triangle.zig b/examples/triangle/triangle.zig index 0a1da7c..4fcf905 100644 --- a/examples/triangle/triangle.zig +++ b/examples/triangle/triangle.zig @@ -19,7 +19,7 @@ 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); diff --git a/src/adapter.zig b/src/adapter.zig index b319239..1329462 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -222,13 +222,13 @@ 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 instance = try 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_response = adapter.?.requestDeviceSync(instance, null, 200_000_000); const device: ?*Device = switch(device_response.status) { .success => device_response.device, else => null diff --git a/src/instance.zig b/src/instance.zig index 2ec163e..7df0eb9 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -79,7 +79,18 @@ 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: [] u8 = "", + dxc_path: []u8 = "", + dxc_max_shader_model: DxcMaxShaderModel, +}; + +const WGPUInstanceExtras = extern struct { chain: ChainedStruct = ChainedStruct { .s_type = SType.instance_extras, }, @@ -93,7 +104,18 @@ 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, +}; + +const WGPUInstanceCapabilities = extern struct { // This struct chain is used as mutable in some places and immutable in others. next_in_chain: ?*ChainedStructOut = null, @@ -104,17 +126,22 @@ pub const InstanceCapabilities = extern struct { timed_wait_any_max_count: usize, }; -pub const InstanceDescriptor = extern struct { +pub const InstanceDescriptor = struct { + features: InstanceCapabilities, + native_extras: ?InstanceExtras = null, +}; + +const WGPUInstanceDescriptor = extern struct { next_in_chain: ?*const ChainedStruct = null, // Instance features to enable - features: InstanceCapabilities, + features: WGPUInstanceCapabilities, - pub inline fn withNativeExtras(self: InstanceDescriptor, extras: *InstanceExtras) InstanceDescriptor { - var id = self; - id.next_in_chain = @ptrCast(extras); - return id; - } + // pub inline fn withNativeExtras(self: InstanceDescriptor, extras: *InstanceExtras) InstanceDescriptor { + // var id = self; + // id.next_in_chain = @ptrCast(extras); + // return id; + // } }; pub const WGSLLanguageFeatureName = enum(u32) { @@ -124,13 +151,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,27 +168,9 @@ 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 wgpuInstanceHasWGSLLanguageFeature(instance: *Instance, feature: WGSLLanguageFeatureName) WGPUBool; @@ -212,16 +221,59 @@ 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, +} || 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| { + var instance_extras: ?*const ChainedStruct = undefined; + if (d.native_extras) |native_extras| { + instance_extras = @ptrCast(&WGPUInstanceExtras { + .backends = native_extras.backends, + .flags = native_extras.flags, + .dx12_shader_compiler = native_extras.dx12_shader_compiler, + .gles3_minor_version = native_extras.gles3_minor_version, + .gl_fence_behavior = native_extras.gl_fence_behavior, + .dxil_path = StringView.fromSlice(native_extras.dxil_path), + .dxc_path = StringView.fromSlice(native_extras.dxc_path), + .dxc_max_shader_model = native_extras.dxc_max_shader_model, + }); + } else { + instance_extras = null; + } + + maybe_instance = wgpuCreateInstance(&WGPUInstanceDescriptor { + .next_in_chain = instance_extras, + .features = WGPUInstanceCapabilities { + .timed_wait_any_enable = @intFromBool(d.features.timed_wait_any_enable), + .timed_wait_any_max_count = d.features.timed_wait_any_max_count, + }, + }); + } 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 InstanceCapabilities { + .next_in_chain = wgpu_capabilities.next_in_chain, + .timed_wait_any_enable = wgpu_capabilities.timed_wait_any_enable != 0, + .timed_wait_any_max_count = wgpu_capabilities.timed_wait_any_max_count, + }; + } else { + return InstanceError.FailedToGetCapabilities; + } } pub inline fn createSurface(self: *Instance, descriptor: *const SurfaceDescriptor) ?*Surface { @@ -305,27 +357,40 @@ 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 = Instance.create(null); - try testing.expect(instance != null); - instance.?.release(); + const instance = try Instance.create(null); + instance.release(); } test "can request adapter" { const testing = @import("std").testing; - const instance = Instance.create(null); - const response = instance.?.requestAdapterSync(null, 200_000_000); + const instance = try 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); +} + +test "can enumerate adapters" { + const testing = @import("std").testing; + + 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/tests/compute.zig b/tests/compute.zig index b0f9474..5b41b70 100644 --- a/tests/compute.zig +++ b/tests/compute.zig @@ -14,7 +14,7 @@ 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); From 1f27a43fddd3e9120a7c3a3b63cbc3421791c30a Mon Sep 17 00:00:00 2001 From: TotoShampoin Date: Tue, 1 Jul 2025 12:38:19 +0200 Subject: [PATCH 02/21] Similar initial work for Device structs/methods --- examples/triangle/triangle.zig | 24 ++--- src/adapter.zig | 37 ++++++-- src/device.zig | 161 +++++++++++++++++---------------- tests/compute.zig | 28 +++--- 4 files changed, 142 insertions(+), 108 deletions(-) diff --git a/examples/triangle/triangle.zig b/examples/triangle/triangle.zig index 4fcf905..2eb0040 100644 --- a/examples/triangle/triangle.zig +++ b/examples/triangle/triangle.zig @@ -29,7 +29,7 @@ pub fn main() !void { }; defer adapter.release(); - const device_request = adapter.requestDeviceSync(instance, &wgpu.DeviceDescriptor { + const device_request = adapter.requestDeviceSync(instance, .{ .required_limits = null, }, 0); const device = switch(device_request.status) { @@ -38,17 +38,17 @@ pub fn main() !void { }; 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, - }).?; + }); defer target_texture.release(); const target_texture_view = target_texture.createView(&wgpu.TextureViewDescriptor { @@ -57,17 +57,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, .size = output_size, .mapped_at_creation = @as(u32, @intFromBool(false)), - }).?; + }); defer staging_buffer.release(); const color_targets = &[_] wgpu.ColorTargetState{ @@ -88,7 +88,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 +101,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{ diff --git a/src/adapter.zig b/src/adapter.zig index 1329462..402384d 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -20,6 +20,8 @@ const Instance = @import("instance.zig").Instance; const _device = @import("device.zig"); const Device = _device.Device; const DeviceDescriptor = _device.DeviceDescriptor; +const WGPUDeviceDescriptor = _device.WGPUDeviceDescriptor; +const WGPUDeviceExtras = _device.WGPUDeviceExtras; const RequestDeviceCallback = _device.RequestDeviceCallback; const RequestDeviceCallbackInfo = _device.RequestDeviceCallbackInfo; const RequestDeviceStatus = _device.RequestDeviceStatus; @@ -145,7 +147,7 @@ pub const AdapterProcs = struct { 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 RequestDevice = *const fn(*Adapter, ?*const WGPUDeviceDescriptor, RequestDeviceCallbackInfo) callconv(.C) Future; pub const AddRef = *const fn(*Adapter) callconv(.C) void; pub const Release = *const fn(*Adapter) callconv(.C) void; }; @@ -154,7 +156,7 @@ extern fn wgpuAdapterGetFeatures(adapter: *Adapter, features: *SupportedFeatures extern fn wgpuAdapterGetLimits(adapter: *Adapter, limits: *Limits) Status; extern fn wgpuAdapterGetInfo(adapter: *Adapter, info: *AdapterInfo) 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; @@ -186,7 +188,7 @@ pub const Adapter = opaque{ // 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 { + pub fn requestDeviceSync(self: *Adapter, instance: *Instance, descriptor: ?DeviceDescriptor, polling_interval_nanoseconds: u64) RequestDeviceResponse { var response: RequestDeviceResponse = undefined; var completed = false; const callback_info = RequestDeviceCallbackInfo { @@ -194,7 +196,7 @@ pub const Adapter = opaque{ .userdata1 = @ptrCast(&response), .userdata2 = @ptrCast(&completed), }; - const device_future = wgpuAdapterRequestDevice(self, descriptor, callback_info); + 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. @@ -208,8 +210,31 @@ pub const Adapter = opaque{ return response; } - pub inline fn requestDevice(self: *Adapter, descriptor: ?*const DeviceDescriptor, callback_info: RequestDeviceCallbackInfo) Future { - return wgpuAdapterRequestDevice(self, descriptor, callback_info); + pub inline fn requestDevice(self: *Adapter, descriptor: ?DeviceDescriptor, callback_info: RequestDeviceCallbackInfo) Future { + if(descriptor) |d| { + var device_extras: ?*const ChainedStruct = undefined; + if(d.native_extras) |native_extras| { + device_extras = @ptrCast(&WGPUDeviceExtras { + .trace_path = .fromSlice(native_extras.trace_path), + }); + } else { + device_extras = null; + } + + return wgpuAdapterRequestDevice(self, &WGPUDeviceDescriptor{ + .next_in_chain = device_extras, + .label = .fromSlice(d.label), + .required_feature_count = d.required_features.len, + .required_features = d.required_features.ptr, + .required_limits = if(d.required_limits) |l| &l else null, + .default_queue = d.default_queue, + .device_lost_callback_info = d.device_lost_callback_info, + .uncaptured_error_callback_info = d.uncaptured_error_callback_info, + }, callback_info); + } else { + return wgpuAdapterRequestDevice(self, null, callback_info); + } + } pub inline fn addRef(self: *Adapter) void { wgpuAdapterAddRef(self); diff --git a/src/device.zig b/src/device.zig index 4fccbb5..30e5f0f 100644 --- a/src/device.zig +++ b/src/device.zig @@ -105,7 +105,11 @@ pub fn defaultDeviceLostCallback(device: *const ?*Device, reason: DeviceLostReas std.debug.panic("Device lost: reason={s} message=\"{s}\"\n", .{ @tagName(reason), message.toSlice() orelse "" }); } -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, }, @@ -135,7 +139,17 @@ pub const UncapturedErrorCallbackInfo = extern struct { userdata2: ?*anyopaque = null, }; -pub const DeviceDescriptor = extern struct { +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 const WGPUDeviceDescriptor = extern struct { next_in_chain: ?*const ChainedStruct = null, label: StringView = StringView {}, required_feature_count: usize = 0, @@ -145,13 +159,13 @@ 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 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 const RequestDeviceStatus = enum(u32) { @@ -223,39 +237,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 +268,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 +309,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 +328,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 +347,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 +371,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 +402,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/tests/compute.zig b/tests/compute.zig index 5b41b70..9907060 100644 --- a/tests/compute.zig +++ b/tests/compute.zig @@ -25,50 +25,50 @@ fn compute_collatz() ![4]u32 { defer adapter.release(); const device_response = adapter.requestDeviceSync(instance, null, 200_000_000); - const device = switch(device_response.status) { + const device: *wgpu.Device = switch(device_response.status) { .success => device_response.device.?, else => return error.NoDevice, }; 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, .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, .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 +80,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 { From c6929f74d5ad00459b9fec3fd23f4c5ce7c3e819 Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Wed, 2 Jul 2025 02:59:06 -0500 Subject: [PATCH 03/21] use const for u8 slices --- src/instance.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/instance.zig b/src/instance.zig index 7df0eb9..7da73f1 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -85,8 +85,8 @@ pub const InstanceExtras = struct { dx12_shader_compiler: Dx12Compiler, gles3_minor_version: Gles3MinorVersion, gl_fence_behavior: GLFenceBehaviour, - dxil_path: [] u8 = "", - dxc_path: []u8 = "", + dxil_path: []const u8 = "", + dxc_path: []const u8 = "", dxc_max_shader_model: DxcMaxShaderModel, }; From 61a42674252f05ab8f1fbbbf5e3c4ca7ea2868e4 Mon Sep 17 00:00:00 2001 From: TotoShampoin Date: Wed, 2 Jul 2025 20:05:16 +0200 Subject: [PATCH 04/21] More idiomatic Adapter structs and methods --- examples/triangle/triangle.zig | 2 +- src/adapter.zig | 84 ++++++++++++++++++++++++---------- src/device.zig | 2 +- src/instance.zig | 12 +++-- 4 files changed, 71 insertions(+), 29 deletions(-) diff --git a/examples/triangle/triangle.zig b/examples/triangle/triangle.zig index 2eb0040..67f7b18 100644 --- a/examples/triangle/triangle.zig +++ b/examples/triangle/triangle.zig @@ -22,7 +22,7 @@ pub fn main() !void { const instance = try wgpu.Instance.create(null); defer instance.release(); - const adapter_request = instance.requestAdapterSync(&wgpu.RequestAdapterOptions {}, 0); + const adapter_request = instance.requestAdapterSync(wgpu.RequestAdapterOptions {}, 0); const adapter = switch(adapter_request.status) { .success => adapter_request.adapter.?, else => return error.NoAdapter, diff --git a/src/adapter.zig b/src/adapter.zig index 402384d..de49f03 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -120,13 +120,35 @@ pub const RequestAdapterResponse = struct { 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, @@ -137,38 +159,54 @@ 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 WGPUDeviceDescriptor, 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 WGPUDeviceDescriptor, callback_info: RequestDeviceCallbackInfo) Future; extern fn wgpuAdapterAddRef(adapter: *Adapter) void; extern fn wgpuAdapterRelease(adapter: *Adapter) void; +pub const AdapterError = error { + FailedToGetLimits, + FailedToGetInfo, +} || 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{}; + 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.FailtedToGetLimits; + 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.FailtedToGetAdapterInfo; + 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; diff --git a/src/device.zig b/src/device.zig index 30e5f0f..07efba7 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; diff --git a/src/instance.zig b/src/instance.zig index 7da73f1..6d8f7b0 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -311,7 +311,7 @@ pub const Instance = opaque { // 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 { + pub fn requestAdapterSync(self: *Instance, options: ?RequestAdapterOptions, polling_interval_nanoseconds: u64) RequestAdapterResponse { var response: RequestAdapterResponse = undefined; var completed = false; const callback_info = RequestAdapterCallbackInfo { @@ -319,7 +319,7 @@ pub const Instance = opaque { .userdata1 = @ptrCast(&response), .userdata2 = @ptrCast(&completed), }; - const adapter_future = wgpuInstanceRequestAdapter(self, options, callback_info); + 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. @@ -333,8 +333,12 @@ pub const Instance = opaque { return response; } - pub inline fn requestAdapter(self: *Instance, options: ?*const RequestAdapterOptions, callback_info: RequestAdapterCallbackInfo) Future { - return wgpuInstanceRequestAdapter(self, options, callback_info); + pub inline fn requestAdapter(self: *Instance, options: ?RequestAdapterOptions, callback_info: RequestAdapterCallbackInfo) Future { + if(options) |o| { + return wgpuInstanceRequestAdapter(self, &o, callback_info); + } else { + return wgpuInstanceRequestAdapter(self, null, callback_info); + } } // Unimplemented as of wgpu-native v25.0.2.1, From 3da9f41e1f18bab8a81c17d6677706814318f418 Mon Sep 17 00:00:00 2001 From: TotoShampoin Date: Wed, 2 Jul 2025 23:00:16 +0200 Subject: [PATCH 05/21] Fixed typo --- src/adapter.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/adapter.zig b/src/adapter.zig index de49f03..2dd58be 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -189,13 +189,13 @@ pub const Adapter = opaque{ pub inline fn getLimits(self: *Adapter) AdapterError!Limits { var limits = Limits{}; if(wgpuAdapterGetLimits(self, &limits) == .@"error") - return error.FailtedToGetLimits; + return error.FailedToGetLimits; return limits; } pub inline fn getInfo(self: *Adapter) AdapterError!AdapterInfo { var adapter_info = WGPUAdapterInfo{}; if(wgpuAdapterGetInfo(self, &adapter_info) == .@"error") - return error.FailtedToGetAdapterInfo; + return error.FailedToGetAdapterInfo; return AdapterInfo{ .next_in_chain = adapter_info.next_in_chain, .vendor = adapter_info.vendor.toSlice(), From 3fe903e5c6f1b44451bbb2dfdcdffccad9b303cb Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Fri, 4 Jul 2025 04:48:49 -0500 Subject: [PATCH 06/21] Try new callback format on Instance.requestAdapter() --- examples/triangle/triangle.zig | 6 +-- src/adapter.zig | 37 +++++++++++++--- src/instance.zig | 80 +++++++++++++++++++--------------- tests/compute.zig | 6 +-- 4 files changed, 78 insertions(+), 51 deletions(-) diff --git a/examples/triangle/triangle.zig b/examples/triangle/triangle.zig index 67f7b18..4c2143a 100644 --- a/examples/triangle/triangle.zig +++ b/examples/triangle/triangle.zig @@ -22,11 +22,7 @@ pub fn main() !void { 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, .{ diff --git a/src/adapter.zig b/src/adapter.zig index 2dd58be..764fa51 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -86,7 +86,7 @@ pub const RequestAdapterOptions = extern struct { compatible_surface: ?*Surface = null, }; -pub const RequestAdapterStatus = enum(u32) { +const RequestAdapterStatus = enum(u32) { success = 0x00000001, instance_dropped = 0x00000002, unavailable = 0x00000003, @@ -94,6 +94,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, @@ -114,6 +121,26 @@ pub const RequestAdapterCallback = *const fn( userdata2: ?*anyopaque, ) callconv(.C) void; +pub fn MakeRequestAdapterCallbackTrampoline( + comptime UserDataPointerType: type, +) type { + const CallbackType = *const fn(RequestAdapterError!*Adapter, ?[]const u8, UserDataPointerType) void; + return struct { + pub fn callback(status: RequestAdapterStatus, adapter: ?*Adapter, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { + const wrapped_callback: CallbackType = @ptrCast(userdata2); + const userdata: UserDataPointerType = @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); + } + }; +} + pub const RequestAdapterResponse = struct { status: RequestAdapterStatus, message: ?[]const u8, @@ -286,12 +313,8 @@ test "can request device" { const testing = @import("std").testing; const instance = try 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 adapter = try instance.requestAdapterSync(null, 200_000_000); + const device_response = adapter.requestDeviceSync(instance, null, 200_000_000); const device: ?*Device = switch(device_response.status) { .success => device_response.device, else => null diff --git a/src/instance.zig b/src/instance.zig index 6d8f7b0..6db1f79 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -10,9 +10,10 @@ const Adapter = _adapter.Adapter; const RequestAdapterOptions = _adapter.RequestAdapterOptions; const RequestAdapterCallbackInfo = _adapter.RequestAdapterCallbackInfo; const RequestAdapterCallback = _adapter.RequestAdapterCallback; -const RequestAdapterStatus = _adapter.RequestAdapterStatus; +const RequestAdapterError = _adapter.RequestAdapterError; const RequestAdapterResponse = _adapter.RequestAdapterResponse; const BackendType = _adapter.BackendType; +const MakeRequestAdapterCallbackTrampoline = _adapter.MakeRequestAdapterCallbackTrampoline; const _surface = @import("surface.zig"); const Surface = _surface.Surface; @@ -28,6 +29,7 @@ const _async = @import("async.zig"); const Future = _async.Future; const WaitStatus = _async.WaitStatus; const FutureWaitInfo = _async.FutureWaitInfo; +const CallbackMode = _async.CallbackMode; pub const InstanceBackend = WGPUFlags; pub const InstanceBackends = struct { @@ -172,7 +174,7 @@ extern fn wgpuGetInstanceCapabilities(capabilities: *WGPUInstanceCapabilities) S 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; @@ -224,7 +226,7 @@ extern fn wgpuInstanceEnumerateAdapters(instance: *Instance, options: ?*Enumerat pub const InstanceError = error { FailedToCreateInstance, FailedToGetCapabilities, -} || std.mem.Allocator.Error; +} || RequestAdapterError || std.mem.Allocator.Error; pub const Instance = opaque { // This is a global function, but it creates an instance so I put it here. @@ -297,44 +299,60 @@ 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: ?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 = self.requestAdapter(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 adapter_future = self.requestAdapter( + null, + &adapter_response, + defaultAdapterCallback, + options, + ); // 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: ?RequestAdapterOptions, callback_info: RequestAdapterCallbackInfo) Future { - if(options) |o| { + + pub fn requestAdapter( + self: *Instance, + mode: ?CallbackMode, + userdata: anytype, + callback: *const fn(RequestAdapterError!*Adapter, ?[]const u8, @TypeOf(userdata)) void, + options: ?RequestAdapterOptions, + ) Future { + if (@typeInfo(@TypeOf(userdata)) != .pointer) { + @compileError("userdata should be a pointer type"); + } + const Trampoline = MakeRequestAdapterCallbackTrampoline(@TypeOf(userdata)); + const callback_info = RequestAdapterCallbackInfo { + .mode = mode orelse CallbackMode.allow_process_events, + .callback = Trampoline.callback, + .userdata1 = @ptrCast(userdata), + .userdata2 = @constCast(@ptrCast(callback)), + }; + if (options) |o| { return wgpuInstanceRequestAdapter(self, &o, callback_info); } else { return wgpuInstanceRequestAdapter(self, null, callback_info); @@ -378,16 +396,10 @@ test "can create instance (and release it afterwards)" { instance.release(); } -test "can request adapter" { - const testing = @import("std").testing; - +test "requestAdapterSync returns adapter" { const instance = try 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 response = try instance.requestAdapterSync(null, 0); + _ = response; } test "can enumerate adapters" { diff --git a/tests/compute.zig b/tests/compute.zig index 9907060..fd792b7 100644 --- a/tests/compute.zig +++ b/tests/compute.zig @@ -17,11 +17,7 @@ fn compute_collatz() ![4]u32 { 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); From 40a48f39bdbaa23e838ca7f41102cf5c7e13e2e5 Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Fri, 4 Jul 2025 07:04:18 -0500 Subject: [PATCH 07/21] Implement new callback format for Adapter.requestDevice() --- examples/triangle/triangle.zig | 6 +-- src/adapter.zig | 83 +++++++++++++++++----------------- src/device.zig | 41 +++++++++++------ src/instance.zig | 1 - tests/compute.zig | 6 +-- 5 files changed, 70 insertions(+), 67 deletions(-) diff --git a/examples/triangle/triangle.zig b/examples/triangle/triangle.zig index 4c2143a..25c4e4b 100644 --- a/examples/triangle/triangle.zig +++ b/examples/triangle/triangle.zig @@ -25,13 +25,9 @@ pub fn main() !void { const adapter = try instance.requestAdapterSync(wgpu.RequestAdapterOptions {}, 0); defer adapter.release(); - const device_request = adapter.requestDeviceSync(instance, .{ + 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 = try device.getQueue(); diff --git a/src/adapter.zig b/src/adapter.zig index 764fa51..6dbc9bc 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -22,10 +22,9 @@ const Device = _device.Device; const DeviceDescriptor = _device.DeviceDescriptor; const WGPUDeviceDescriptor = _device.WGPUDeviceDescriptor; const WGPUDeviceExtras = _device.WGPUDeviceExtras; -const RequestDeviceCallback = _device.RequestDeviceCallback; const RequestDeviceCallbackInfo = _device.RequestDeviceCallbackInfo; -const RequestDeviceStatus = _device.RequestDeviceStatus; -const RequestDeviceResponse = _device.RequestDeviceResponse; +const RequestDeviceError = _device.RequestDeviceError; +const MakeRequestDeviceCallbackTrampoline = _device.MakeRequestDeviceCallbackTrampoline; const _async = @import("async.zig"); const CallbackMode = _async.CallbackMode; @@ -141,12 +140,6 @@ pub fn MakeRequestAdapterCallbackTrampoline( }; } -pub const RequestAdapterResponse = struct { - status: RequestAdapterStatus, - message: ?[]const u8, - adapter: ?*Adapter, -}; - extern fn wgpuAdapterInfoFreeMembers(adapter_info: WGPUAdapterInfo) void; pub const AdapterInfo = struct { @@ -202,7 +195,7 @@ extern fn wgpuAdapterRelease(adapter: *Adapter) void; pub const AdapterError = error { FailedToGetLimits, FailedToGetInfo, -} || std.mem.Allocator.Error; +} || RequestDeviceError || std.mem.Allocator.Error; pub const Adapter = opaque{ pub inline fn getFeatures(self: *Adapter, allocator: std.mem.Allocator) AdapterError![]FeatureName { @@ -239,43 +232,57 @@ pub const Adapter = opaque{ 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: ?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 = self.requestDevice(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 device_future = self.requestDevice( + null, + &device_response, + defaultDeviceCallback, + descriptor, + ); // 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: ?DeviceDescriptor, callback_info: RequestDeviceCallbackInfo) Future { + pub fn requestDevice( + self: *Adapter, + mode: ?CallbackMode, + userdata: anytype, + callback: *const fn(RequestDeviceError!*Device, ?[]const u8, @TypeOf(userdata)) void, + descriptor: ?DeviceDescriptor, + ) Future { + if (@typeInfo(@TypeOf(userdata)) != .pointer) { + @compileError("userdata should be a pointer type"); + } + const Trampoline = MakeRequestDeviceCallbackTrampoline(@TypeOf(userdata)); + const callback_info = RequestDeviceCallbackInfo { + .mode = mode orelse CallbackMode.allow_process_events, + .callback = Trampoline.callback, + .userdata1 = @ptrCast(userdata), + .userdata2 = @constCast(@ptrCast(callback)), + }; + if(descriptor) |d| { var device_extras: ?*const ChainedStruct = undefined; if(d.native_extras) |native_extras| { @@ -299,8 +306,8 @@ pub const Adapter = opaque{ } else { return wgpuAdapterRequestDevice(self, null, callback_info); } - } + pub inline fn addRef(self: *Adapter) void { wgpuAdapterAddRef(self); } @@ -310,14 +317,8 @@ pub const Adapter = opaque{ }; test "can request device" { - const testing = @import("std").testing; - const instance = try Instance.create(null); - const adapter = try instance.requestAdapterSync(null, 200_000_000); - const device_response = adapter.requestDeviceSync(instance, null, 200_000_000); - const device: ?*Device = switch(device_response.status) { - .success => device_response.device, - else => null - }; - try testing.expect(device != null); + const adapter = try instance.requestAdapterSync(null, 0); + const device = try adapter.requestDeviceSync(instance, null, 0); + _ = device; } \ No newline at end of file diff --git a/src/device.zig b/src/device.zig index 07efba7..a88d1b5 100644 --- a/src/device.zig +++ b/src/device.zig @@ -158,23 +158,21 @@ pub const WGPUDeviceDescriptor = extern struct { default_queue: QueueDescriptor = QueueDescriptor{}, 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 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, @@ -184,12 +182,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, @@ -201,6 +193,25 @@ pub const RequestDeviceCallbackInfo = extern struct { userdata2: ?*anyopaque = null, }; +pub fn MakeRequestDeviceCallbackTrampoline( + comptime UserDataPointerType: type, +) type { + const CallbackType = *const fn(RequestDeviceError!*Device, ?[]const u8, UserDataPointerType) void; + return struct { + pub fn callback(status: RequestDeviceStatus, device: ?*Device, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { + const wrapped_callback: CallbackType = @ptrCast(userdata2); + const userdata: UserDataPointerType = @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); + } + }; +} + pub const PopErrorScopeStatus = enum(u32) { success = 0x00000001, // The error scope stack was successfully popped and a result was reported. instance_dropped = 0x00000002, diff --git a/src/instance.zig b/src/instance.zig index 6db1f79..83bfc07 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -11,7 +11,6 @@ const RequestAdapterOptions = _adapter.RequestAdapterOptions; const RequestAdapterCallbackInfo = _adapter.RequestAdapterCallbackInfo; const RequestAdapterCallback = _adapter.RequestAdapterCallback; const RequestAdapterError = _adapter.RequestAdapterError; -const RequestAdapterResponse = _adapter.RequestAdapterResponse; const BackendType = _adapter.BackendType; const MakeRequestAdapterCallbackTrampoline = _adapter.MakeRequestAdapterCallbackTrampoline; diff --git a/tests/compute.zig b/tests/compute.zig index fd792b7..3934987 100644 --- a/tests/compute.zig +++ b/tests/compute.zig @@ -20,11 +20,7 @@ fn compute_collatz() ![4]u32 { const adapter = try instance.requestAdapterSync(null, 0); defer adapter.release(); - const device_response = adapter.requestDeviceSync(instance, null, 200_000_000); - const device: *wgpu.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 = try device.getQueue(); From 9751d0c1959dc22f05fb9f4b87f208be9e6bf331 Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Sat, 5 Jul 2025 00:30:48 -0500 Subject: [PATCH 08/21] Implement toWGPU() method for structs in instance.zig --- src/instance.zig | 79 +++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/src/instance.zig b/src/instance.zig index 83bfc07..cf4d51c 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -89,6 +89,19 @@ pub const InstanceExtras = struct { 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 { @@ -114,6 +127,13 @@ pub const InstanceCapabilities = struct { // 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 { @@ -125,11 +145,34 @@ const WGPUInstanceCapabilities = extern struct { // The maximum number FutureWaitInfo supported in a call to ::wgpuInstanceWaitAny with `timeoutNS > 0`. timed_wait_any_max_count: usize, + + 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; + } + + return WGPUInstanceDescriptor { + .next_in_chain = instance_extras, + .features = self.features.toWGPU(), + }; + } }; const WGPUInstanceDescriptor = extern struct { @@ -137,12 +180,6 @@ const WGPUInstanceDescriptor = extern struct { // Instance features to enable features: WGPUInstanceCapabilities, - - // pub inline fn withNativeExtras(self: InstanceDescriptor, extras: *InstanceExtras) InstanceDescriptor { - // var id = self; - // id.next_in_chain = @ptrCast(extras); - // return id; - // } }; pub const WGSLLanguageFeatureName = enum(u32) { @@ -232,29 +269,7 @@ pub const Instance = opaque { pub fn create(descriptor: ?InstanceDescriptor) InstanceError!*Instance { var maybe_instance: ?*Instance = undefined; if (descriptor) |d| { - var instance_extras: ?*const ChainedStruct = undefined; - if (d.native_extras) |native_extras| { - instance_extras = @ptrCast(&WGPUInstanceExtras { - .backends = native_extras.backends, - .flags = native_extras.flags, - .dx12_shader_compiler = native_extras.dx12_shader_compiler, - .gles3_minor_version = native_extras.gles3_minor_version, - .gl_fence_behavior = native_extras.gl_fence_behavior, - .dxil_path = StringView.fromSlice(native_extras.dxil_path), - .dxc_path = StringView.fromSlice(native_extras.dxc_path), - .dxc_max_shader_model = native_extras.dxc_max_shader_model, - }); - } else { - instance_extras = null; - } - - maybe_instance = wgpuCreateInstance(&WGPUInstanceDescriptor { - .next_in_chain = instance_extras, - .features = WGPUInstanceCapabilities { - .timed_wait_any_enable = @intFromBool(d.features.timed_wait_any_enable), - .timed_wait_any_max_count = d.features.timed_wait_any_max_count, - }, - }); + maybe_instance = wgpuCreateInstance(&d.toWGPU()); } else { maybe_instance = wgpuCreateInstance(null); } @@ -267,11 +282,7 @@ pub const Instance = opaque { pub inline fn getCapabilities() InstanceError!InstanceCapabilities { var wgpu_capabilities: WGPUInstanceCapabilities = undefined; if (wgpuGetInstanceCapabilities(&wgpu_capabilities) == Status.success) { - return InstanceCapabilities { - .next_in_chain = wgpu_capabilities.next_in_chain, - .timed_wait_any_enable = wgpu_capabilities.timed_wait_any_enable != 0, - .timed_wait_any_max_count = wgpu_capabilities.timed_wait_any_max_count, - }; + return wgpu_capabilities.toInstanceCapabilities(); } else { return InstanceError.FailedToGetCapabilities; } From 6a6510597562f47ce84c4c579e56ff8c8f17c164 Mon Sep 17 00:00:00 2001 From: TotoShampoin Date: Sat, 5 Jul 2025 16:48:41 +0200 Subject: [PATCH 09/21] Implement toWGPU() method for structs in adapter.zig and device.zig --- src/adapter.zig | 56 ++++++++++++++++++++++++++++++++---------------- src/device.zig | 22 +++++++++++++++++++ src/instance.zig | 5 +++-- 3 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/adapter.zig b/src/adapter.zig index 6dbc9bc..dace752 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -72,6 +72,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), @@ -284,25 +320,7 @@ pub const Adapter = opaque{ }; if(descriptor) |d| { - var device_extras: ?*const ChainedStruct = undefined; - if(d.native_extras) |native_extras| { - device_extras = @ptrCast(&WGPUDeviceExtras { - .trace_path = .fromSlice(native_extras.trace_path), - }); - } else { - device_extras = null; - } - - return wgpuAdapterRequestDevice(self, &WGPUDeviceDescriptor{ - .next_in_chain = device_extras, - .label = .fromSlice(d.label), - .required_feature_count = d.required_features.len, - .required_features = d.required_features.ptr, - .required_limits = if(d.required_limits) |l| &l else null, - .default_queue = d.default_queue, - .device_lost_callback_info = d.device_lost_callback_info, - .uncaptured_error_callback_info = d.uncaptured_error_callback_info, - }, callback_info); + return wgpuAdapterRequestDevice(self, &d.toWGPU(), callback_info); } else { return wgpuAdapterRequestDevice(self, null, callback_info); } diff --git a/src/device.zig b/src/device.zig index a88d1b5..abb9bdd 100644 --- a/src/device.zig +++ b/src/device.zig @@ -147,6 +147,28 @@ pub const DeviceDescriptor = struct { device_lost_callback_info: DeviceLostCallbackInfo = DeviceLostCallbackInfo {}, uncaptured_error_callback_info: UncapturedErrorCallbackInfo = UncapturedErrorCallbackInfo{}, native_extras: ?DeviceExtras = null, + + pub fn toWGPU(self: 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; + } + + 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 = if(self.required_limits) |l| &l else null, + .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 { diff --git a/src/instance.zig b/src/instance.zig index cf4d51c..8fd26a6 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -8,6 +8,7 @@ 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 RequestAdapterError = _adapter.RequestAdapterError; @@ -213,7 +214,7 @@ extern fn wgpuInstanceCreateSurface(instance: *Instance, descriptor: *const Surf 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; @@ -363,7 +364,7 @@ pub const Instance = opaque { .userdata2 = @constCast(@ptrCast(callback)), }; if (options) |o| { - return wgpuInstanceRequestAdapter(self, &o, callback_info); + return wgpuInstanceRequestAdapter(self, &o.toWGPU(), callback_info); } else { return wgpuInstanceRequestAdapter(self, null, callback_info); } From b4ad365ceffe0b0ca663bf99799460849c817962 Mon Sep 17 00:00:00 2001 From: TotoShampoin Date: Sat, 5 Jul 2025 17:09:10 +0200 Subject: [PATCH 10/21] Implement toWGPU() method for structs in adapter.zig --- src/adapter.zig | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/instance.zig | 5 +++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/adapter.zig b/src/adapter.zig index 6dbc9bc..2398780 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -72,6 +72,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), @@ -83,6 +119,17 @@ 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, + }; + } }; const RequestAdapterStatus = enum(u32) { diff --git a/src/instance.zig b/src/instance.zig index cf4d51c..8fd26a6 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -8,6 +8,7 @@ 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 RequestAdapterError = _adapter.RequestAdapterError; @@ -213,7 +214,7 @@ extern fn wgpuInstanceCreateSurface(instance: *Instance, descriptor: *const Surf 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; @@ -363,7 +364,7 @@ pub const Instance = opaque { .userdata2 = @constCast(@ptrCast(callback)), }; if (options) |o| { - return wgpuInstanceRequestAdapter(self, &o, callback_info); + return wgpuInstanceRequestAdapter(self, &o.toWGPU(), callback_info); } else { return wgpuInstanceRequestAdapter(self, null, callback_info); } From c05d469aea583146d686deb6b870ceb8d0b27a67 Mon Sep 17 00:00:00 2001 From: TotoShampoin Date: Sat, 5 Jul 2025 17:15:21 +0200 Subject: [PATCH 11/21] Implement toWGPU() method for structs in device.zig --- src/adapter.zig | 20 +------------------- src/device.zig | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/adapter.zig b/src/adapter.zig index 2398780..e114ee3 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -331,25 +331,7 @@ pub const Adapter = opaque{ }; if(descriptor) |d| { - var device_extras: ?*const ChainedStruct = undefined; - if(d.native_extras) |native_extras| { - device_extras = @ptrCast(&WGPUDeviceExtras { - .trace_path = .fromSlice(native_extras.trace_path), - }); - } else { - device_extras = null; - } - - return wgpuAdapterRequestDevice(self, &WGPUDeviceDescriptor{ - .next_in_chain = device_extras, - .label = .fromSlice(d.label), - .required_feature_count = d.required_features.len, - .required_features = d.required_features.ptr, - .required_limits = if(d.required_limits) |l| &l else null, - .default_queue = d.default_queue, - .device_lost_callback_info = d.device_lost_callback_info, - .uncaptured_error_callback_info = d.uncaptured_error_callback_info, - }, callback_info); + return wgpuAdapterRequestDevice(self, &d.toWGPU(), callback_info); } else { return wgpuAdapterRequestDevice(self, null, callback_info); } diff --git a/src/device.zig b/src/device.zig index a88d1b5..e573610 100644 --- a/src/device.zig +++ b/src/device.zig @@ -147,6 +147,28 @@ pub const DeviceDescriptor = struct { device_lost_callback_info: DeviceLostCallbackInfo = DeviceLostCallbackInfo {}, uncaptured_error_callback_info: UncapturedErrorCallbackInfo = UncapturedErrorCallbackInfo{}, native_extras: ?DeviceExtras = null, + + pub fn toWGPU(self: 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; + } + + 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 = if(self.required_limits) |l| &l else null, + .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 { @@ -158,6 +180,24 @@ pub const WGPUDeviceDescriptor = extern struct { default_queue: QueueDescriptor = QueueDescriptor{}, device_lost_callback_info: DeviceLostCallbackInfo = DeviceLostCallbackInfo {}, uncaptured_error_callback_info: UncapturedErrorCallbackInfo = UncapturedErrorCallbackInfo{}, + + 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, + }; + } }; const RequestDeviceStatus = enum(u32) { From fb8013478e159937d12fc2e48b0e2b9f9c591865 Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Wed, 16 Jul 2025 13:42:34 -0500 Subject: [PATCH 12/21] Test and related bugfixes for DeviceDescriptor.toWGPU() --- src/adapter.zig | 34 +++++++++++++++++++++++++++++++++- src/device.zig | 9 +++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/adapter.zig b/src/adapter.zig index e114ee3..a2f5c58 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -246,7 +246,7 @@ pub const AdapterError = error { pub const Adapter = opaque{ pub inline fn getFeatures(self: *Adapter, allocator: std.mem.Allocator) AdapterError![]FeatureName { - var features = SupportedFeatures{}; + var features: SupportedFeatures = undefined; defer features.freeMembers(); wgpuAdapterGetFeatures(self, &features); @@ -349,5 +349,37 @@ test "can request device" { 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"), + }, + + // TODO: Revisit these after refatoring callbacks for those two. + .device_lost_callback_info = .{}, + .uncaptured_error_callback_info = .{}, + }; + + const device = try adapter.requestDeviceSync(instance, descriptor, 0); + _ = device; } \ No newline at end of file diff --git a/src/device.zig b/src/device.zig index e573610..c592323 100644 --- a/src/device.zig +++ b/src/device.zig @@ -148,7 +148,7 @@ pub const DeviceDescriptor = struct { uncaptured_error_callback_info: UncapturedErrorCallbackInfo = UncapturedErrorCallbackInfo{}, native_extras: ?DeviceExtras = null, - pub fn toWGPU(self: DeviceDescriptor) WGPUDeviceDescriptor { + pub fn toWGPU(self: *const DeviceDescriptor) WGPUDeviceDescriptor { var device_extras: ?*const ChainedStruct = undefined; if(self.native_extras) |native_extras| { device_extras = @ptrCast(&WGPUDeviceExtras { @@ -158,12 +158,17 @@ pub const DeviceDescriptor = struct { 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 = if(self.required_limits) |l| &l else null, + .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, From 1ad74c41c6835dbe9e083b885122507552259480 Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Wed, 16 Jul 2025 19:25:34 -0500 Subject: [PATCH 13/21] Run unit tests from root to avoid duplicate test runs --- build.zig | 82 +++++++++++++++++++++++-------------------------------- test-all | 2 +- 2 files changed, 35 insertions(+), 49 deletions(-) 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/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 From fe8bbc7f33ef29b65541be00c3ea08b4c3bdf954 Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Wed, 16 Jul 2025 19:51:48 -0500 Subject: [PATCH 14/21] refAllDecls from root --- src/root.zig | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/root.zig b/src/root.zig index e20ae4c..7aa5e1a 100644 --- a/src/root.zig +++ b/src/root.zig @@ -22,13 +22,9 @@ 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"); @@ -99,14 +95,11 @@ 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"); @@ -122,9 +115,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; @@ -306,3 +297,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 From 2ab86e16d04b77704e999798cc483d99900c56d7 Mon Sep 17 00:00:00 2001 From: TotoShampoin Date: Thu, 17 Jul 2025 10:05:10 +0200 Subject: [PATCH 15/21] Using packed structs instead of ORed consts --- examples/triangle/triangle.zig | 6 +++--- src/buffer.zig | 38 ++++++++++++++++++---------------- src/instance.zig | 35 ++++++++++++++++--------------- src/pipeline.zig | 19 +++++++++-------- src/root.zig | 7 ------- src/shader.zig | 13 ++++++------ src/surface.zig | 4 ++-- src/texture.zig | 19 +++++++++-------- tests/compute.zig | 6 +++--- 9 files changed, 73 insertions(+), 74 deletions(-) diff --git a/examples/triangle/triangle.zig b/examples/triangle/triangle.zig index e6a472f..e544c74 100644 --- a/examples/triangle/triangle.zig +++ b/examples/triangle/triangle.zig @@ -39,7 +39,7 @@ pub fn main() !void { .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(); @@ -56,7 +56,7 @@ pub fn main() !void { 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)), }); @@ -147,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/buffer.zig b/src/buffer.zig index a09e84a..69a4fa7 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) { diff --git a/src/instance.zig b/src/instance.zig index 8fd26a6..a915212 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -31,25 +31,26 @@ const WaitStatus = _async.WaitStatus; const FutureWaitInfo = _async.FutureWaitInfo; const CallbackMode = _async.CallbackMode; -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; +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 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) { diff --git a/src/pipeline.zig b/src/pipeline.zig index 2f244a5..fd72dbe 100644 --- a/src/pipeline.zig +++ b/src/pipeline.zig @@ -381,14 +381,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 +401,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 { diff --git a/src/root.zig b/src/root.zig index 7aa5e1a..c48db08 100644 --- a/src/root.zig +++ b/src/root.zig @@ -44,10 +44,8 @@ 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; @@ -104,9 +102,7 @@ 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; @@ -166,7 +162,6 @@ 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; @@ -212,7 +207,6 @@ 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; @@ -271,7 +265,6 @@ 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; diff --git a/src/shader.zig b/src/shader.zig index bc5209e..ff15f98 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 { diff --git a/src/surface.zig b/src/surface.zig index dd99cc2..708b733 100644 --- a/src/surface.zig +++ b/src/surface.zig @@ -10,7 +10,7 @@ const _texture = @import("texture.zig"); const Texture = _texture.Texture; const TextureFormat = _texture.TextureFormat; const TextureUsage = _texture.TextureUsage; -const TextureUsages = _texture.TextureUsages; +// const TextureUsages = _texture.TextureUsages; const _device = @import("device.zig"); const Device = _device.Device; @@ -246,7 +246,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, diff --git a/src/texture.zig b/src/texture.zig index 951d8c3..7f06697 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,7 +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, + usage: TextureUsage = TextureUsage.none, }; pub const TextureViewProcs = struct { diff --git a/tests/compute.zig b/tests/compute.zig index 3934987..336b509 100644 --- a/tests/compute.zig +++ b/tests/compute.zig @@ -34,7 +34,7 @@ fn compute_collatz() ![4]u32 { 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)), }); @@ -42,7 +42,7 @@ fn compute_collatz() ![4]u32 { 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)), }); @@ -103,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), }); From 44173799d9e37a07a6cc357085e180fc21234553 Mon Sep 17 00:00:00 2001 From: TotoShampoin Date: Thu, 17 Jul 2025 19:00:58 +0200 Subject: [PATCH 16/21] Added a missing `all` const --- src/instance.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/instance.zig b/src/instance.zig index a915212..30e3171 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -40,6 +40,7 @@ pub const InstanceBackend = packed struct(WGPUFlags) { 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 }; }; From 9a42a59806a2a3d9fc1d5e77febaddb4b492313f Mon Sep 17 00:00:00 2001 From: TotoShampoin Date: Thu, 17 Jul 2025 19:02:16 +0200 Subject: [PATCH 17/21] Removed commented out code --- src/surface.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/surface.zig b/src/surface.zig index 708b733..90cb93c 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; From e78ba419585a2c471fc967b1fec6c06bafab4eaf Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Thu, 17 Jul 2025 17:53:59 -0500 Subject: [PATCH 18/21] Remove all Procs --- README.md | 4 +-- src/bind_group.zig | 12 ------- src/buffer.zig | 14 -------- src/command_encoder.zig | 80 ----------------------------------------- src/global.zig | 18 ---------- src/misc.zig | 4 --- src/pipeline.zig | 20 ----------- src/query_set.zig | 9 ----- src/queue.zig | 13 ------- src/render_bundle.zig | 27 -------------- src/root.zig | 20 ----------- src/sampler.zig | 6 ---- src/shader.zig | 7 ---- src/surface.zig | 15 -------- src/texture.zig | 22 ------------ 15 files changed, 1 insertion(+), 270 deletions(-) delete mode 100644 src/global.zig 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/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 69a4fa7..a51a425 100644 --- a/src/buffer.zig +++ b/src/buffer.zig @@ -87,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/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/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 fd72dbe..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; @@ -425,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 c48db08..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; @@ -31,12 +30,10 @@ 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"); @@ -50,7 +47,6 @@ 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"); @@ -74,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"); @@ -134,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; @@ -142,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; @@ -165,7 +156,6 @@ pub const ColorWriteMask = _pipeline.ColorWriteMask; 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; @@ -175,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"); @@ -184,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"); @@ -202,7 +188,6 @@ 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"); @@ -225,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"); @@ -252,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"); @@ -267,7 +249,6 @@ pub const TextureFormat = _texture.TextureFormat; pub const TextureUsage = _texture.TextureUsage; 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; @@ -277,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; 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 ff15f98..1d1a216 100644 --- a/src/shader.zig +++ b/src/shader.zig @@ -164,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 90cb93c..4d5b77d 100644 --- a/src/surface.zig +++ b/src/surface.zig @@ -272,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. @@ -346,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 7f06697..c596e94 100644 --- a/src/texture.zig +++ b/src/texture.zig @@ -154,12 +154,6 @@ pub const TextureViewDescriptor = extern struct { usage: TextureUsage = TextureUsage.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; -}; - extern fn wgpuTextureViewSetLabel(texture_view: *TextureView, label: StringView) void; extern fn wgpuTextureViewAddRef(texture_view: *TextureView) void; extern fn wgpuTextureViewRelease(texture_view: *TextureView) void; @@ -256,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; From d86fa0068823d8977d9453cc7d0d9765a9048aee Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Thu, 17 Jul 2025 22:39:40 -0500 Subject: [PATCH 19/21] Added some constructor functions for DeviceLostCallbackInfo and UncapturedErrorCallbackInfo --- src/adapter.zig | 1 - src/device.zig | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/adapter.zig b/src/adapter.zig index a2f5c58..56b39e3 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -374,7 +374,6 @@ test "can request device with descriptor" { .label = StringView.fromSlice("test queue"), }, - // TODO: Revisit these after refatoring callbacks for those two. .device_lost_callback_info = .{}, .uncaptured_error_callback_info = .{}, }; diff --git a/src/device.zig b/src/device.zig index c592323..b40142c 100644 --- a/src/device.zig +++ b/src/device.zig @@ -105,6 +105,30 @@ pub fn defaultDeviceLostCallback(device: *const ?*Device, reason: DeviceLostReas std.debug.panic("Device lost: reason={s} message=\"{s}\"\n", .{ @tagName(reason), message.toSlice() orelse "" }); } +pub fn deviceLostCallbackInfo( + userdata: anytype, + callback: *const fn(device: ?*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: ?*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)), + }; +} + pub const DeviceExtras = struct { trace_path: []const u8, }; @@ -139,6 +163,30 @@ pub const UncapturedErrorCallbackInfo = extern struct { userdata2: ?*anyopaque = null, }; +pub fn uncapturedErrorCallbackInfo( + 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 = struct { label: []const u8 = "", required_features: []const FeatureName = &[0]FeatureName {}, From 9930d3f1776cb2b93dd6848eb67947fecdcbd802 Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Fri, 18 Jul 2025 16:06:18 -0500 Subject: [PATCH 20/21] Add tests for deviceLostCallbackInfo() and uncapturedErrorCallbackInfo() and fix related bugs --- src/device.zig | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/device.zig b/src/device.zig index b40142c..3f8b405 100644 --- a/src/device.zig +++ b/src/device.zig @@ -107,7 +107,7 @@ pub fn defaultDeviceLostCallback(device: *const ?*Device, reason: DeviceLostReas pub fn deviceLostCallbackInfo( userdata: anytype, - callback: *const fn(device: ?*Device, reason: DeviceLostReason, message: []const u8, _userdata: @TypeOf(userdata)) void, + callback: *const fn(device: *const ?*Device, reason: DeviceLostReason, message: ?[]const u8, _userdata: @TypeOf(userdata)) void, ) DeviceLostCallbackInfo { const UserDataType = @TypeOf(userdata); const CallbackType = @TypeOf(callback); @@ -115,7 +115,7 @@ pub fn deviceLostCallbackInfo( @compileError("userdata should be a pointer type"); } const Trampoline = struct { - fn cb(device: ?*Device, reason: DeviceLostReason, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { + 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); @@ -129,6 +129,23 @@ pub fn deviceLostCallbackInfo( }; } +test "deviceLostCallbackInfo 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(&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 = struct { trace_path: []const u8, }; @@ -149,6 +166,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, @@ -158,14 +182,14 @@ 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 uncapturedErrorCallbackInfo( userdata: anytype, - callback: *const fn(device: ?*Device, error_type: ErrorType, message: []const u8, _userdata: @TypeOf(userdata)) void, + callback: *const fn(device: ?*Device, error_type: ErrorType, message: ?[]const u8, _userdata: @TypeOf(userdata)) void, ) UncapturedErrorCallbackInfo { const UserDataType = @TypeOf(userdata); const CallbackType = @TypeOf(callback); @@ -187,6 +211,22 @@ pub fn uncapturedErrorCallbackInfo( }; } +test "uncapturedErrorCallbackInfo 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(&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 {}, From bebc63b2b8b4b5e937710e60cd07ca710af73439 Mon Sep 17 00:00:00 2001 From: Gabriel Charleboix Date: Thu, 24 Jul 2025 23:27:09 -0500 Subject: [PATCH 21/21] Tweak CallbackInfo struct construction a bit more Add init() method so that we don't have both a function and a struct with the same name --- src/adapter.zig | 75 ++++++++++---------- src/device.zig | 176 ++++++++++++++++++++++++++--------------------- src/instance.zig | 22 ++---- 3 files changed, 143 insertions(+), 130 deletions(-) diff --git a/src/adapter.zig b/src/adapter.zig index 56b39e3..03ca0f6 100644 --- a/src/adapter.zig +++ b/src/adapter.zig @@ -24,7 +24,6 @@ const WGPUDeviceDescriptor = _device.WGPUDeviceDescriptor; const WGPUDeviceExtras = _device.WGPUDeviceExtras; const RequestDeviceCallbackInfo = _device.RequestDeviceCallbackInfo; const RequestDeviceError = _device.RequestDeviceError; -const MakeRequestDeviceCallbackTrampoline = _device.MakeRequestDeviceCallbackTrampoline; const _async = @import("async.zig"); const CallbackMode = _async.CallbackMode; @@ -156,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. @@ -167,25 +201,6 @@ pub const RequestAdapterCallback = *const fn( userdata2: ?*anyopaque, ) callconv(.C) void; -pub fn MakeRequestAdapterCallbackTrampoline( - comptime UserDataPointerType: type, -) type { - const CallbackType = *const fn(RequestAdapterError!*Adapter, ?[]const u8, UserDataPointerType) void; - return struct { - pub fn callback(status: RequestAdapterStatus, adapter: ?*Adapter, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { - const wrapped_callback: CallbackType = @ptrCast(userdata2); - const userdata: UserDataPointerType = @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); - } - }; -} extern fn wgpuAdapterInfoFreeMembers(adapter_info: WGPUAdapterInfo) void; @@ -293,11 +308,14 @@ pub const Adapter = opaque{ pub fn requestDeviceSync(self: *Adapter, instance: *Instance, descriptor: ?DeviceDescriptor, polling_interval_nanoseconds: u64) AdapterError!*Device { var device_response: ?RequestDeviceError!*Device = null; - const device_future = self.requestDevice( + 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, @@ -314,22 +332,9 @@ pub const Adapter = opaque{ pub fn requestDevice( self: *Adapter, - mode: ?CallbackMode, - userdata: anytype, - callback: *const fn(RequestDeviceError!*Device, ?[]const u8, @TypeOf(userdata)) void, descriptor: ?DeviceDescriptor, + callback_info: RequestDeviceCallbackInfo, ) Future { - if (@typeInfo(@TypeOf(userdata)) != .pointer) { - @compileError("userdata should be a pointer type"); - } - const Trampoline = MakeRequestDeviceCallbackTrampoline(@TypeOf(userdata)); - const callback_info = RequestDeviceCallbackInfo { - .mode = mode orelse CallbackMode.allow_process_events, - .callback = Trampoline.callback, - .userdata1 = @ptrCast(userdata), - .userdata2 = @constCast(@ptrCast(callback)), - }; - if(descriptor) |d| { return wgpuAdapterRequestDevice(self, &d.toWGPU(), callback_info); } else { diff --git a/src/device.zig b/src/device.zig index 3f8b405..7d05f01 100644 --- a/src/device.zig +++ b/src/device.zig @@ -77,20 +77,6 @@ pub const DeviceLostReason = enum(u32) { failed_creation = 0x00000004, }; -pub const DeviceLostCallbackInfo = extern struct { - next_in_chain: ?*ChainedStruct = null, - - // Apparently in the webgpu header this has no (valid) default: https://github.com/webgpu-native/webgpu-headers/pull/471 - // As of wgpu-native v24.0.3.1, Instance.waitAny() has not been implemented, but Instance.processEvents() has, - // so the safest mode to use currently is probably CallbackMode.allow_process_events. - // If you really know what you're doing, CallbackMode.allow_spontaneous could also work as an option here. - // TODO: Revisit this if/when Instance.waitAny() is implemented in wgpu-native - mode: CallbackMode = CallbackMode.allow_process_events, - callback: DeviceLostCallback = defaultDeviceLostCallback, - userdata1: ?*anyopaque = null, - userdata2: ?*anyopaque = null, -}; - // `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 { @@ -105,31 +91,46 @@ pub fn defaultDeviceLostCallback(device: *const ?*Device, reason: DeviceLostReas std.debug.panic("Device lost: reason={s} message=\"{s}\"\n", .{ @tagName(reason), message.toSlice() orelse "" }); } -pub fn deviceLostCallbackInfo( - 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); +pub const DeviceLostCallbackInfo = extern struct { + next_in_chain: ?*ChainedStruct = null, + + // Apparently in the webgpu header this has no (valid) default: https://github.com/webgpu-native/webgpu-headers/pull/471 + // As of wgpu-native v24.0.3.1, Instance.waitAny() has not been implemented, but Instance.processEvents() has, + // so the safest mode to use currently is probably CallbackMode.allow_process_events. + // If you really know what you're doing, CallbackMode.allow_spontaneous could also work as an option here. + // TODO: Revisit this if/when Instance.waitAny() is implemented in wgpu-native + mode: CallbackMode = CallbackMode.allow_process_events, + 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)), - }; -} + return DeviceLostCallbackInfo { + .callback = Trampoline.cb, + .userdata1 = @ptrCast(userdata), + .userdata2 = @constCast(@ptrCast(callback)), + }; + } +}; -test "deviceLostCallbackInfo constructs valid DeviceLostCallbackInfo struct with custom callback" { + +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; @@ -141,7 +142,7 @@ test "deviceLostCallbackInfo constructs valid DeviceLostCallbackInfo struct with var callback_called = false; const not_device: ?*Device = null; - const cb_info = deviceLostCallbackInfo(&callback_called, CBStruct.cb); + 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); } @@ -185,33 +186,34 @@ pub const UncapturedErrorCallbackInfo = extern struct { callback: UncapturedErrorCallback = defaultUncapturedErrorCallback, userdata1: ?*anyopaque = null, userdata2: ?*anyopaque = null, -}; -pub fn uncapturedErrorCallbackInfo( - 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); + 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)), + }; + } +}; - return UncapturedErrorCallbackInfo { - .callback = Trampoline.cb, - .userdata1 = @ptrCast(userdata), - .userdata2 = @constCast(@ptrCast(callback)), - }; -} -test "uncapturedErrorCallbackInfo constructs valid UncapturedErrorCallbackInfo struct with custom callback" { +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; @@ -222,7 +224,7 @@ test "uncapturedErrorCallbackInfo constructs valid UncapturedErrorCallbackInfo s }; var callback_called = false; - const cb_info = uncapturedErrorCallbackInfo(&callback_called, CBStruct.cb); + 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); } @@ -324,26 +326,42 @@ pub const RequestDeviceCallbackInfo = extern struct { callback: RequestDeviceCallback, userdata1: ?*anyopaque = null, userdata2: ?*anyopaque = null, -}; -pub fn MakeRequestDeviceCallbackTrampoline( - comptime UserDataPointerType: type, -) type { - const CallbackType = *const fn(RequestDeviceError!*Device, ?[]const u8, UserDataPointerType) void; - return struct { - pub fn callback(status: RequestDeviceStatus, device: ?*Device, message: StringView, userdata1: ?*anyopaque, userdata2: ?*anyopaque) callconv(.C) void { - const wrapped_callback: CallbackType = @ptrCast(userdata2); - const userdata: UserDataPointerType = @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); + 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. diff --git a/src/instance.zig b/src/instance.zig index 30e3171..af34ce3 100644 --- a/src/instance.zig +++ b/src/instance.zig @@ -13,7 +13,6 @@ const RequestAdapterCallbackInfo = _adapter.RequestAdapterCallbackInfo; const RequestAdapterCallback = _adapter.RequestAdapterCallback; const RequestAdapterError = _adapter.RequestAdapterError; const BackendType = _adapter.BackendType; -const MakeRequestAdapterCallbackTrampoline = _adapter.MakeRequestAdapterCallbackTrampoline; const _surface = @import("surface.zig"); const Surface = _surface.Surface; @@ -328,11 +327,14 @@ pub const Instance = opaque { pub fn requestAdapterSync(self: *Instance, options: ?RequestAdapterOptions, polling_interval_nanoseconds: u64) InstanceError!*Adapter { var adapter_response: ?RequestAdapterError!*Adapter = null; - const adapter_future = self.requestAdapter( - 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, @@ -350,21 +352,9 @@ pub const Instance = opaque { pub fn requestAdapter( self: *Instance, - mode: ?CallbackMode, - userdata: anytype, - callback: *const fn(RequestAdapterError!*Adapter, ?[]const u8, @TypeOf(userdata)) void, options: ?RequestAdapterOptions, + callback_info: RequestAdapterCallbackInfo, ) Future { - if (@typeInfo(@TypeOf(userdata)) != .pointer) { - @compileError("userdata should be a pointer type"); - } - const Trampoline = MakeRequestAdapterCallbackTrampoline(@TypeOf(userdata)); - const callback_info = RequestAdapterCallbackInfo { - .mode = mode orelse CallbackMode.allow_process_events, - .callback = Trampoline.callback, - .userdata1 = @ptrCast(userdata), - .userdata2 = @constCast(@ptrCast(callback)), - }; if (options) |o| { return wgpuInstanceRequestAdapter(self, &o.toWGPU(), callback_info); } else {