From bab338d16028bb3c1e513af875698dda2381a5a8 Mon Sep 17 00:00:00 2001 From: Stephen Gutekanst Date: Sat, 26 Feb 2022 20:29:48 -0700 Subject: [PATCH] gpu-dawn: port newer createSurfaceForWindow API Helps hexops/mach#121 Signed-off-by: Stephen Gutekanst --- gpu-dawn/src/dawn/sample_utils.zig | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/gpu-dawn/src/dawn/sample_utils.zig b/gpu-dawn/src/dawn/sample_utils.zig index ad1e5258..c1383f41 100644 --- a/gpu-dawn/src/dawn/sample_utils.zig +++ b/gpu-dawn/src/dawn/sample_utils.zig @@ -2,6 +2,9 @@ const std = @import("std"); const assert = std.debug.assert; const glfw = @import("glfw"); const c = @import("c.zig").c; +const objc = @cImport({ + @cInclude("objc/message.h"); +}); fn printDeviceError(error_type: c.WGPUErrorType, message: [*c]const u8, _: ?*anyopaque) callconv(.C) void { switch (error_type) { @@ -146,6 +149,95 @@ fn discoverAdapter(instance: c.MachDawnNativeInstance, window: glfw.Window, typ: } } +pub fn detectGLFWOptions() glfw.BackendOptions { + const target = @import("builtin").target; + if (target.isDarwin()) return .{ .cocoa = true }; + return switch (target.os.tag) { + .windows => .{ .win32 = true }, + .linux => .{ .x11 = true }, + else => .{}, + }; +} + +pub fn createSurfaceForWindow( + instance: c.WGPUInstance, + window: glfw.Window, + comptime glfw_options: glfw.BackendOptions, +) c.WGPUSurface { + const glfw_native = glfw.Native(glfw_options); + if (glfw_options.win32) { + var desc: c.WGPUSurfaceDescriptorFromWindowsHWND = undefined; + desc.chain.next = null; + desc.chain.sType = c.WGPUSType_SurfaceDescriptorFromWindowsHWND; + + desc.hinstance = c.GetModuleHandle(null); + desc.hwnd = glfw_native.getWin32Window(window); + + var descriptor: c.WGPUSurfaceDescriptor = undefined; + descriptor.nextInChain = @ptrCast(*c.WGPUChainedStruct, &desc); + descriptor.label = "basic surface"; + return c.wgpuInstanceCreateSurface(instance, &descriptor); + } else if (glfw_options.x11) { + var desc: c.WGPUSurfaceDescriptorFromXlibWindow = undefined; + desc.chain.next = null; + desc.chain.sType = c.WGPUSType_SurfaceDescriptorFromXlibWindow; + + desc.display = glfw_native.getX11Display(); + desc.window = glfw_native.getX11Window(window); + + var descriptor: c.WGPUSurfaceDescriptor = undefined; + descriptor.nextInChain = @ptrCast(*c.WGPUChainedStruct, &desc); + descriptor.label = "basic surface"; + return c.wgpuInstanceCreateSurface(instance, &descriptor); + } else if (glfw_options.cocoa) { + var desc: c.WGPUSurfaceDescriptorFromMetalLayer = undefined; + desc.chain.next = null; + desc.chain.sType = c.WGPUSType_SurfaceDescriptorFromMetalLayer; + + const ns_window = glfw_native.getCocoaWindow(window); + const ns_view = msgSend(ns_window, "contentView", .{}, *anyopaque); // [nsWindow contentView] + + // Create a CAMetalLayer that covers the whole window that will be passed to CreateSurface. + msgSend(ns_view, "setWantsLayer:", .{true}, void); // [view setWantsLayer:YES] + const layer = msgSend(objc.objc_getClass("CAMetalLayer"), "layer", .{}, ?*anyopaque); // [CAMetalLayer layer] + if (layer == null) @panic("failed to create Metal layer"); + msgSend(ns_view, "setLayer:", .{layer.?}, void); // [view setLayer:layer] + + // Use retina if the window was created with retina support. + const scale_factor = msgSend(ns_window, "backingScaleFactor", .{}, f64); // [ns_window backingScaleFactor] + msgSend(layer.?, "setContentsScale:", .{scale_factor}, void); // [layer setContentsScale:scale_factor] + + desc.layer = layer.?; + + var descriptor: c.WGPUSurfaceDescriptor = undefined; + descriptor.nextInChain = @ptrCast(*c.WGPUChainedStruct, &desc); + descriptor.label = "basic surface"; + return c.wgpuInstanceCreateSurface(instance, &descriptor); + } else if (glfw_options.wayland) { + @panic("Dawn does not yet have Wayland support, see https://bugs.chromium.org/p/dawn/issues/detail?id=1246&q=surface&can=2"); + } else unreachable; +} + +// Borrowed from https://github.com/hazeycode/zig-objcrt +pub fn msgSend(obj: anytype, sel_name: [:0]const u8, args: anytype, comptime ReturnType: type) ReturnType { + const args_meta = @typeInfo(@TypeOf(args)).Struct.fields; + + const FnType = switch (args_meta.len) { + 0 => fn (@TypeOf(obj), objc.SEL) callconv(.C) ReturnType, + 1 => fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type) callconv(.C) ReturnType, + 2 => fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type, args_meta[1].field_type) callconv(.C) ReturnType, + 3 => fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type, args_meta[1].field_type, args_meta[2].field_type) callconv(.C) ReturnType, + 4 => fn (@TypeOf(obj), objc.SEL, args_meta[0].field_type, args_meta[1].field_type, args_meta[2].field_type, args_meta[3].field_type) callconv(.C) ReturnType, + else => @compileError("Unsupported number of args"), + }; + + // NOTE: func is a var because making it const causes a compile error which I believe is a compiler bug + var func = @ptrCast(FnType, objc.objc_msgSend); + const sel = objc.sel_getUid(sel_name); + + return @call(.{}, func, .{ obj, sel } ++ args); +} + // wgpu::TextureFormat GetPreferredSwapChainTextureFormat() { // DoFlush(); // return static_cast(binding->GetPreferredSwapChainTextureFormat());