gpu: add gpu-hello-triangle (dawn) example
Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
parent
29b83b3118
commit
718a66497c
6 changed files with 490 additions and 2 deletions
|
|
@ -1,17 +1,28 @@
|
|||
const std = @import("std");
|
||||
const gpu_dawn = @import("libs/mach-gpu-dawn/build.zig");
|
||||
const glfw = @import("libs/mach-glfw/build.zig");
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
// Standard release options allow the person running `zig build` to select
|
||||
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||
const mode = b.standardReleaseOptions();
|
||||
const target = b.standardTargetOptions(.{});
|
||||
|
||||
const lib = b.addStaticLibrary("gpu", "src/main.zig");
|
||||
lib.setBuildMode(mode);
|
||||
lib.install();
|
||||
gpu_dawn.link(b, lib, .{});
|
||||
|
||||
const main_tests = b.addTest("src/main.zig");
|
||||
main_tests.setBuildMode(mode);
|
||||
|
||||
const test_step = b.step("test", "Run library tests");
|
||||
test_step.dependOn(&main_tests.step);
|
||||
|
||||
const example = b.addExecutable("gpu-hello-triangle", "examples/main.zig");
|
||||
example.setTarget(target);
|
||||
example.setBuildMode(mode);
|
||||
example.install();
|
||||
example.linkLibC();
|
||||
example.addPackagePath("glfw", "libs/mach-glfw/src/main.zig");
|
||||
glfw.link(b, example, .{});
|
||||
gpu_dawn.link(b, example, .{});
|
||||
}
|
||||
|
|
|
|||
5
gpu/examples/c.zig
Normal file
5
gpu/examples/c.zig
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub const c = @cImport({
|
||||
@cInclude("dawn/webgpu.h");
|
||||
@cInclude("dawn/dawn_proc.h");
|
||||
@cInclude("dawn_native_mach.h");
|
||||
});
|
||||
234
gpu/examples/main.zig
Normal file
234
gpu/examples/main.zig
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
const std = @import("std");
|
||||
const sample_utils = @import("sample_utils.zig");
|
||||
const c = @import("c.zig").c;
|
||||
const glfw = @import("glfw");
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
var allocator = gpa.allocator();
|
||||
|
||||
const setup = try sample_utils.setup(allocator);
|
||||
const queue = c.wgpuDeviceGetQueue(setup.device);
|
||||
const framebuffer_size = try setup.window.getFramebufferSize();
|
||||
|
||||
const window_data = try allocator.create(WindowData);
|
||||
window_data.* = .{
|
||||
.surface = null,
|
||||
.swap_chain = null,
|
||||
.swap_chain_format = undefined,
|
||||
.current_desc = undefined,
|
||||
.target_desc = undefined,
|
||||
};
|
||||
setup.window.setUserPointer(window_data);
|
||||
|
||||
// If targetting OpenGL, we can't use the newer WGPUSurface API. Instead, we need to use the
|
||||
// older Dawn-specific API. https://bugs.chromium.org/p/dawn/issues/detail?id=269&q=surface&can=2
|
||||
const use_legacy_api = setup.backend_type == c.WGPUBackendType_OpenGL or setup.backend_type == c.WGPUBackendType_OpenGLES;
|
||||
var descriptor: c.WGPUSwapChainDescriptor = undefined;
|
||||
if (!use_legacy_api) {
|
||||
window_data.swap_chain_format = c.WGPUTextureFormat_BGRA8Unorm;
|
||||
descriptor = c.WGPUSwapChainDescriptor{
|
||||
.nextInChain = null,
|
||||
.label = "basic swap chain",
|
||||
.usage = c.WGPUTextureUsage_RenderAttachment,
|
||||
.format = window_data.swap_chain_format,
|
||||
.width = framebuffer_size.width,
|
||||
.height = framebuffer_size.height,
|
||||
.presentMode = c.WGPUPresentMode_Fifo,
|
||||
.implementation = 0,
|
||||
};
|
||||
window_data.surface = sample_utils.createSurfaceForWindow(
|
||||
setup.instance,
|
||||
setup.window,
|
||||
comptime sample_utils.detectGLFWOptions(),
|
||||
);
|
||||
} else {
|
||||
const binding = c.machUtilsCreateBinding(setup.backend_type, @ptrCast(*c.GLFWwindow, setup.window.handle), setup.device);
|
||||
if (binding == null) {
|
||||
@panic("failed to create Dawn backend binding");
|
||||
}
|
||||
descriptor = std.mem.zeroes(c.WGPUSwapChainDescriptor);
|
||||
descriptor.implementation = c.machUtilsBackendBinding_getSwapChainImplementation(binding);
|
||||
window_data.swap_chain = c.wgpuDeviceCreateSwapChain(setup.device, null, &descriptor);
|
||||
|
||||
window_data.swap_chain_format = c.machUtilsBackendBinding_getPreferredSwapChainTextureFormat(binding);
|
||||
c.wgpuSwapChainConfigure(
|
||||
window_data.swap_chain.?,
|
||||
window_data.swap_chain_format,
|
||||
c.WGPUTextureUsage_RenderAttachment,
|
||||
framebuffer_size.width,
|
||||
framebuffer_size.height,
|
||||
);
|
||||
}
|
||||
window_data.current_desc = descriptor;
|
||||
window_data.target_desc = descriptor;
|
||||
|
||||
const vs =
|
||||
\\ @stage(vertex) fn main(
|
||||
\\ @builtin(vertex_index) VertexIndex : u32
|
||||
\\ ) -> @builtin(position) vec4<f32> {
|
||||
\\ var pos = array<vec2<f32>, 3>(
|
||||
\\ vec2<f32>( 0.0, 0.5),
|
||||
\\ vec2<f32>(-0.5, -0.5),
|
||||
\\ vec2<f32>( 0.5, -0.5)
|
||||
\\ );
|
||||
\\ return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
|
||||
\\ }
|
||||
;
|
||||
var vs_wgsl_descriptor = try allocator.create(c.WGPUShaderModuleWGSLDescriptor);
|
||||
vs_wgsl_descriptor.chain.next = null;
|
||||
vs_wgsl_descriptor.chain.sType = c.WGPUSType_ShaderModuleWGSLDescriptor;
|
||||
vs_wgsl_descriptor.source = vs;
|
||||
const vs_shader_descriptor = c.WGPUShaderModuleDescriptor{
|
||||
.nextInChain = @ptrCast(*const c.WGPUChainedStruct, vs_wgsl_descriptor),
|
||||
.label = "my vertex shader",
|
||||
};
|
||||
const vs_module = c.wgpuDeviceCreateShaderModule(setup.device, &vs_shader_descriptor);
|
||||
|
||||
const fs =
|
||||
\\ @stage(fragment) fn main() -> @location(0) vec4<f32> {
|
||||
\\ return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
\\ }
|
||||
;
|
||||
var fs_wgsl_descriptor = try allocator.create(c.WGPUShaderModuleWGSLDescriptor);
|
||||
fs_wgsl_descriptor.chain.next = null;
|
||||
fs_wgsl_descriptor.chain.sType = c.WGPUSType_ShaderModuleWGSLDescriptor;
|
||||
fs_wgsl_descriptor.source = fs;
|
||||
const fs_shader_descriptor = c.WGPUShaderModuleDescriptor{
|
||||
.nextInChain = @ptrCast(*const c.WGPUChainedStruct, fs_wgsl_descriptor),
|
||||
.label = "my fragment shader",
|
||||
};
|
||||
const fs_module = c.wgpuDeviceCreateShaderModule(setup.device, &fs_shader_descriptor);
|
||||
|
||||
// Fragment state
|
||||
var blend = std.mem.zeroes(c.WGPUBlendState);
|
||||
blend.color.operation = c.WGPUBlendOperation_Add;
|
||||
blend.color.srcFactor = c.WGPUBlendFactor_One;
|
||||
blend.color.dstFactor = c.WGPUBlendFactor_One;
|
||||
blend.alpha.operation = c.WGPUBlendOperation_Add;
|
||||
blend.alpha.srcFactor = c.WGPUBlendFactor_One;
|
||||
blend.alpha.dstFactor = c.WGPUBlendFactor_One;
|
||||
|
||||
var color_target = std.mem.zeroes(c.WGPUColorTargetState);
|
||||
color_target.format = window_data.swap_chain_format;
|
||||
color_target.blend = &blend;
|
||||
color_target.writeMask = c.WGPUColorWriteMask_All;
|
||||
|
||||
var fragment = std.mem.zeroes(c.WGPUFragmentState);
|
||||
fragment.module = fs_module;
|
||||
fragment.entryPoint = "main";
|
||||
fragment.targetCount = 1;
|
||||
fragment.targets = &color_target;
|
||||
|
||||
var pipeline_descriptor = std.mem.zeroes(c.WGPURenderPipelineDescriptor);
|
||||
pipeline_descriptor.fragment = &fragment;
|
||||
|
||||
// Other state
|
||||
pipeline_descriptor.layout = null;
|
||||
pipeline_descriptor.depthStencil = null;
|
||||
|
||||
pipeline_descriptor.vertex.module = vs_module;
|
||||
pipeline_descriptor.vertex.entryPoint = "main";
|
||||
pipeline_descriptor.vertex.bufferCount = 0;
|
||||
pipeline_descriptor.vertex.buffers = null;
|
||||
|
||||
pipeline_descriptor.multisample.count = 1;
|
||||
pipeline_descriptor.multisample.mask = 0xFFFFFFFF;
|
||||
pipeline_descriptor.multisample.alphaToCoverageEnabled = false;
|
||||
|
||||
pipeline_descriptor.primitive.frontFace = c.WGPUFrontFace_CCW;
|
||||
pipeline_descriptor.primitive.cullMode = c.WGPUCullMode_None;
|
||||
pipeline_descriptor.primitive.topology = c.WGPUPrimitiveTopology_TriangleList;
|
||||
pipeline_descriptor.primitive.stripIndexFormat = c.WGPUIndexFormat_Undefined;
|
||||
|
||||
const pipeline = c.wgpuDeviceCreateRenderPipeline(setup.device, &pipeline_descriptor);
|
||||
|
||||
c.wgpuShaderModuleRelease(vs_module);
|
||||
c.wgpuShaderModuleRelease(fs_module);
|
||||
|
||||
// Reconfigure the swap chain with the new framebuffer width/height, otherwise e.g. the Vulkan
|
||||
// device would be lost after a resize.
|
||||
setup.window.setFramebufferSizeCallback((struct {
|
||||
fn callback(window: glfw.Window, width: u32, height: u32) void {
|
||||
const pl = window.getUserPointer(WindowData);
|
||||
pl.?.target_desc.width = width;
|
||||
pl.?.target_desc.height = height;
|
||||
}
|
||||
}).callback);
|
||||
|
||||
while (!setup.window.shouldClose()) {
|
||||
try frame(.{
|
||||
.window = setup.window,
|
||||
.device = setup.device,
|
||||
.pipeline = pipeline,
|
||||
.queue = queue,
|
||||
});
|
||||
std.time.sleep(16 * std.time.ns_per_ms);
|
||||
}
|
||||
}
|
||||
|
||||
const WindowData = struct {
|
||||
surface: ?c.WGPUSurface,
|
||||
swap_chain: ?c.WGPUSwapChain,
|
||||
swap_chain_format: c.WGPUTextureFormat,
|
||||
current_desc: c.WGPUSwapChainDescriptor,
|
||||
target_desc: c.WGPUSwapChainDescriptor,
|
||||
};
|
||||
|
||||
const FrameParams = struct {
|
||||
window: glfw.Window,
|
||||
device: c.WGPUDevice,
|
||||
pipeline: c.WGPURenderPipeline,
|
||||
queue: c.WGPUQueue,
|
||||
};
|
||||
|
||||
fn isDescriptorEqual(a: c.WGPUSwapChainDescriptor, b: c.WGPUSwapChainDescriptor) bool {
|
||||
return a.usage == b.usage and a.format == b.format and a.width == b.width and a.height == b.height and a.presentMode == b.presentMode;
|
||||
}
|
||||
|
||||
fn frame(params: FrameParams) !void {
|
||||
try glfw.pollEvents();
|
||||
const pl = params.window.getUserPointer(WindowData).?;
|
||||
if (pl.swap_chain == null or !isDescriptorEqual(pl.current_desc, pl.target_desc)) {
|
||||
const use_legacy_api = pl.surface == null;
|
||||
if (!use_legacy_api) {
|
||||
pl.swap_chain = c.wgpuDeviceCreateSwapChain(params.device, pl.surface.?, &pl.target_desc);
|
||||
} else {
|
||||
c.wgpuSwapChainConfigure(
|
||||
pl.swap_chain.?,
|
||||
pl.swap_chain_format,
|
||||
c.WGPUTextureUsage_RenderAttachment,
|
||||
@intCast(u32, pl.target_desc.width),
|
||||
@intCast(u32, pl.target_desc.height),
|
||||
);
|
||||
}
|
||||
pl.current_desc = pl.target_desc;
|
||||
}
|
||||
|
||||
const back_buffer_view = c.wgpuSwapChainGetCurrentTextureView(pl.swap_chain.?);
|
||||
var render_pass_info = std.mem.zeroes(c.WGPURenderPassDescriptor);
|
||||
var color_attachment = std.mem.zeroes(c.WGPURenderPassColorAttachment);
|
||||
color_attachment.view = back_buffer_view;
|
||||
color_attachment.resolveTarget = null;
|
||||
color_attachment.clearValue = c.WGPUColor{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 0.0 };
|
||||
color_attachment.loadOp = c.WGPULoadOp_Clear;
|
||||
color_attachment.storeOp = c.WGPUStoreOp_Store;
|
||||
render_pass_info.colorAttachmentCount = 1;
|
||||
render_pass_info.colorAttachments = &color_attachment;
|
||||
render_pass_info.depthStencilAttachment = null;
|
||||
|
||||
const encoder = c.wgpuDeviceCreateCommandEncoder(params.device, null);
|
||||
const pass = c.wgpuCommandEncoderBeginRenderPass(encoder, &render_pass_info);
|
||||
c.wgpuRenderPassEncoderSetPipeline(pass, params.pipeline);
|
||||
c.wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0);
|
||||
c.wgpuRenderPassEncoderEnd(pass);
|
||||
c.wgpuRenderPassEncoderRelease(pass);
|
||||
|
||||
const commands = c.wgpuCommandEncoderFinish(encoder, null);
|
||||
c.wgpuCommandEncoderRelease(encoder);
|
||||
|
||||
c.wgpuQueueSubmit(params.queue, 1, &commands);
|
||||
c.wgpuCommandBufferRelease(commands);
|
||||
c.wgpuSwapChainPresent(pl.swap_chain.?);
|
||||
c.wgpuTextureViewRelease(back_buffer_view);
|
||||
}
|
||||
236
gpu/examples/sample_utils.zig
Normal file
236
gpu/examples/sample_utils.zig
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
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) {
|
||||
c.WGPUErrorType_Validation => std.debug.print("dawn: validation error: {s}\n", .{message}),
|
||||
c.WGPUErrorType_OutOfMemory => std.debug.print("dawn: out of memory: {s}\n", .{message}),
|
||||
c.WGPUErrorType_Unknown => std.debug.print("dawn: unknown error: {s}\n", .{message}),
|
||||
c.WGPUErrorType_DeviceLost => std.debug.print("dawn: device lost: {s}\n", .{message}),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
const Setup = struct {
|
||||
instance: c.WGPUInstance,
|
||||
backend_type: c.WGPUBackendType,
|
||||
device: c.WGPUDevice,
|
||||
window: glfw.Window,
|
||||
};
|
||||
|
||||
fn getEnvVarOwned(allocator: std.mem.Allocator, key: []const u8) error{ OutOfMemory, InvalidUtf8 }!?[]u8 {
|
||||
return std.process.getEnvVarOwned(allocator, key) catch |err| switch (err) {
|
||||
error.EnvironmentVariableNotFound => @as(?[]u8, null),
|
||||
else => |e| e,
|
||||
};
|
||||
}
|
||||
|
||||
fn detectBackendType(allocator: std.mem.Allocator) !c.WGPUBackendType {
|
||||
const WGPU_BACKEND = try getEnvVarOwned(allocator, "WGPU_BACKEND");
|
||||
if (WGPU_BACKEND) |backend| {
|
||||
defer allocator.free(backend);
|
||||
if (std.ascii.eqlIgnoreCase(backend, "opengl")) return c.WGPUBackendType_OpenGL;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "opengles")) return c.WGPUBackendType_OpenGLES;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "d3d11")) return c.WGPUBackendType_D3D11;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "d3d12")) return c.WGPUBackendType_D3D12;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "metal")) return c.WGPUBackendType_Metal;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "null")) return c.WGPUBackendType_Null;
|
||||
if (std.ascii.eqlIgnoreCase(backend, "vulkan")) return c.WGPUBackendType_Vulkan;
|
||||
@panic("unknown BACKEND type");
|
||||
}
|
||||
|
||||
const target = @import("builtin").target;
|
||||
if (target.isDarwin()) return c.WGPUBackendType_Metal;
|
||||
if (target.os.tag == .windows) return c.WGPUBackendType_D3D12;
|
||||
return c.WGPUBackendType_Vulkan;
|
||||
}
|
||||
|
||||
fn backendTypeString(t: c.WGPUBackendType) []const u8 {
|
||||
return switch (t) {
|
||||
c.WGPUBackendType_OpenGL => "OpenGL",
|
||||
c.WGPUBackendType_OpenGLES => "OpenGLES",
|
||||
c.WGPUBackendType_D3D11 => "D3D11",
|
||||
c.WGPUBackendType_D3D12 => "D3D12",
|
||||
c.WGPUBackendType_Metal => "Metal",
|
||||
c.WGPUBackendType_Null => "Null",
|
||||
c.WGPUBackendType_Vulkan => "Vulkan",
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setup(allocator: std.mem.Allocator) !Setup {
|
||||
const backend_type = try detectBackendType(allocator);
|
||||
|
||||
try glfw.init(.{});
|
||||
|
||||
// Create the test window and discover adapters using it (esp. for OpenGL)
|
||||
var hints = glfwWindowHintsForBackend(backend_type);
|
||||
hints.cocoa_retina_framebuffer = false;
|
||||
const window = try glfw.Window.create(640, 480, "Dawn window", null, null, hints);
|
||||
|
||||
const instance = c.machDawnNativeInstance_init();
|
||||
try discoverAdapter(instance, window, backend_type);
|
||||
|
||||
const adapters = c.machDawnNativeInstance_getAdapters(instance);
|
||||
var backend_adapter: ?c.MachDawnNativeAdapter = null;
|
||||
var i: usize = 0;
|
||||
while (i < c.machDawnNativeAdapters_length(adapters)) : (i += 1) {
|
||||
const adapter = c.machDawnNativeAdapters_index(adapters, i);
|
||||
const properties = c.machDawnNativeAdapter_getProperties(adapter);
|
||||
if (c.machDawnNativeAdapterProperties_getBackendType(properties) == backend_type) {
|
||||
const name = c.machDawnNativeAdapterProperties_getName(properties);
|
||||
const driver_description = c.machDawnNativeAdapterProperties_getDriverDescription(properties);
|
||||
std.debug.print("found {s} adapter: {s}, {s}\n", .{ backendTypeString(backend_type), name, driver_description });
|
||||
backend_adapter = adapter;
|
||||
}
|
||||
}
|
||||
assert(backend_adapter != null);
|
||||
|
||||
const backend_device = c.machDawnNativeAdapter_createDevice(backend_adapter.?, null);
|
||||
const backend_procs = c.machDawnNativeGetProcs();
|
||||
|
||||
c.dawnProcSetProcs(backend_procs);
|
||||
backend_procs.*.deviceSetUncapturedErrorCallback.?(backend_device, printDeviceError, null);
|
||||
return Setup{
|
||||
.instance = c.machDawnNativeInstance_get(instance),
|
||||
.backend_type = backend_type,
|
||||
.device = backend_device,
|
||||
.window = window,
|
||||
};
|
||||
}
|
||||
|
||||
fn glfwWindowHintsForBackend(backend: c.WGPUBackendType) glfw.Window.Hints {
|
||||
return switch (backend) {
|
||||
c.WGPUBackendType_OpenGL => .{
|
||||
// Ask for OpenGL 4.4 which is what the GL backend requires for compute shaders and
|
||||
// texture views.
|
||||
.context_version_major = 4,
|
||||
.context_version_minor = 4,
|
||||
.opengl_forward_compat = true,
|
||||
.opengl_profile = .opengl_core_profile,
|
||||
},
|
||||
c.WGPUBackendType_OpenGLES => .{
|
||||
.context_version_major = 3,
|
||||
.context_version_minor = 1,
|
||||
.client_api = .opengl_es_api,
|
||||
.context_creation_api = .egl_context_api,
|
||||
},
|
||||
else => .{
|
||||
// Without this GLFW will initialize a GL context on the window, which prevents using
|
||||
// the window with other APIs (by crashing in weird ways).
|
||||
.client_api = .no_api,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
fn discoverAdapter(instance: c.MachDawnNativeInstance, window: glfw.Window, typ: c.WGPUBackendType) !void {
|
||||
if (typ == c.WGPUBackendType_OpenGL) {
|
||||
try glfw.makeContextCurrent(window);
|
||||
const adapter_options = c.MachDawnNativeAdapterDiscoveryOptions_OpenGL{
|
||||
.getProc = @ptrCast(fn ([*c]const u8) callconv(.C) ?*anyopaque, glfw.getProcAddress),
|
||||
};
|
||||
_ = c.machDawnNativeInstance_discoverAdapters(instance, typ, &adapter_options);
|
||||
} else if (typ == c.WGPUBackendType_OpenGLES) {
|
||||
try glfw.makeContextCurrent(window);
|
||||
const adapter_options = c.MachDawnNativeAdapterDiscoveryOptions_OpenGLES{
|
||||
.getProc = @ptrCast(fn ([*c]const u8) callconv(.C) ?*anyopaque, glfw.getProcAddress),
|
||||
};
|
||||
_ = c.machDawnNativeInstance_discoverAdapters(instance, typ, &adapter_options);
|
||||
} else {
|
||||
c.machDawnNativeInstance_discoverDefaultAdapters(instance);
|
||||
}
|
||||
}
|
||||
|
||||
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 = std.os.windows.kernel32.GetModuleHandleW(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);
|
||||
}
|
||||
1
gpu/libs/mach-glfw
Symbolic link
1
gpu/libs/mach-glfw
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../glfw
|
||||
1
gpu/libs/mach-gpu-dawn
Symbolic link
1
gpu/libs/mach-gpu-dawn
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../gpu-dawn
|
||||
Loading…
Add table
Add a link
Reference in a new issue