141 lines
4.1 KiB
Zig
141 lines
4.1 KiB
Zig
const std = @import("std");
|
|
const sh = @import("shaders.zig");
|
|
const c = @import("c.zig").c;
|
|
|
|
const GpuAllocator = @This();
|
|
|
|
instance: c.WGPUInstance,
|
|
adapter: c.WGPUAdapter,
|
|
device: c.WGPUDevice,
|
|
queue: c.WGPUQueue,
|
|
|
|
// Lazily created, cached for lifetime of allocator
|
|
_pip_add: c.WGPUComputePipeline = null,
|
|
_pip_scale: c.WGPUComputePipeline = null,
|
|
|
|
pub fn init() !GpuAllocator {
|
|
const instance = c.wgpuCreateInstance(
|
|
&std.mem.zeroes(c.WGPUInstanceDescriptor),
|
|
) orelse return error.NoInstance;
|
|
errdefer c.wgpuInstanceRelease(instance);
|
|
|
|
var ctx = Ctx{};
|
|
_ = c.wgpuInstanceRequestAdapter(
|
|
instance,
|
|
&.{ .powerPreference = c.WGPUPowerPreference_HighPerformance },
|
|
.{ .callback = onAdapter, .userdata1 = &ctx },
|
|
);
|
|
c.wgpuInstanceProcessEvents(instance);
|
|
const adapter = ctx.adapter orelse return error.NoAdapter;
|
|
errdefer c.wgpuAdapterRelease(adapter);
|
|
|
|
_ = c.wgpuAdapterRequestDevice(
|
|
adapter,
|
|
null,
|
|
.{ .callback = onDevice, .userdata1 = &ctx },
|
|
);
|
|
c.wgpuInstanceProcessEvents(instance);
|
|
const device = ctx.device orelse return error.NoDevice;
|
|
|
|
return .{
|
|
.instance = instance,
|
|
.adapter = adapter,
|
|
.device = device,
|
|
.queue = c.wgpuDeviceGetQueue(device),
|
|
};
|
|
}
|
|
|
|
pub fn deinit(self: *GpuAllocator) void {
|
|
if (self._pip_add) |p| c.wgpuComputePipelineRelease(p);
|
|
if (self._pip_scale) |p| c.wgpuComputePipelineRelease(p);
|
|
c.wgpuQueueRelease(self.queue);
|
|
c.wgpuDeviceRelease(self.device);
|
|
c.wgpuAdapterRelease(self.adapter);
|
|
c.wgpuInstanceRelease(self.instance);
|
|
}
|
|
|
|
// ── Internal ─────────────────────────────────────────────────────────────
|
|
|
|
pub fn makeBuffer(
|
|
self: *GpuAllocator,
|
|
bytes: u64,
|
|
usage: c.WGPUBufferUsage,
|
|
) !c.WGPUBuffer {
|
|
return c.wgpuDeviceCreateBuffer(self.device, &.{
|
|
.usage = usage,
|
|
.size = bytes,
|
|
}) orelse error.BufferAlloc;
|
|
}
|
|
|
|
pub fn pipAdd(self: *GpuAllocator) !c.WGPUComputePipeline {
|
|
if (self._pip_add == null)
|
|
self._pip_add = try buildPipeline(self.device, sh.SHADER_ADD);
|
|
return self._pip_add.?;
|
|
}
|
|
|
|
pub fn pipScale(self: *GpuAllocator) !c.WGPUComputePipeline {
|
|
if (self._pip_scale == null)
|
|
self._pip_scale = try buildPipeline(self.device, sh.SHADER_SCALE);
|
|
return self._pip_scale.?;
|
|
}
|
|
|
|
/// Poll until GPU work completes. Use after submit if you need CPU sync.
|
|
pub fn poll(self: *GpuAllocator) void {
|
|
_ = c.wgpuDevicePoll(self.device, 1, null);
|
|
}
|
|
|
|
const Ctx = struct {
|
|
adapter: c.WGPUAdapter = null,
|
|
device: c.WGPUDevice = null,
|
|
};
|
|
|
|
fn onAdapter(
|
|
status: c.WGPURequestAdapterStatus,
|
|
adapter: c.WGPUAdapter,
|
|
_: c.WGPUStringView,
|
|
userdata1: ?*anyopaque,
|
|
_: ?*anyopaque,
|
|
) callconv(.c) void {
|
|
if (status != c.WGPURequestAdapterStatus_Success) {
|
|
std.log.err("Adapter request failed (status={d})", .{status});
|
|
return;
|
|
}
|
|
const ctx: *Ctx = @ptrCast(@alignCast(userdata1.?));
|
|
ctx.adapter = adapter;
|
|
}
|
|
|
|
fn onDevice(
|
|
status: c.WGPURequestDeviceStatus,
|
|
device: c.WGPUDevice,
|
|
_: c.WGPUStringView,
|
|
userdata1: ?*anyopaque,
|
|
_: ?*anyopaque,
|
|
) callconv(.c) void {
|
|
if (status != c.WGPURequestDeviceStatus_Success) {
|
|
std.log.err("Device request failed (status={d})", .{status});
|
|
return;
|
|
}
|
|
const ctx: *Ctx = @ptrCast(@alignCast(userdata1.?));
|
|
ctx.device = device;
|
|
std.debug.print("{?}", .{device});
|
|
}
|
|
|
|
fn buildPipeline(device: c.WGPUDevice, wgsl: []const u8) !c.WGPUComputePipeline {
|
|
var wgsl_src = c.WGPUShaderSourceWGSL{
|
|
.chain = .{ .sType = c.WGPUSType_ShaderSourceWGSL },
|
|
.code = sv(wgsl),
|
|
};
|
|
const shader = c.wgpuDeviceCreateShaderModule(device, &.{
|
|
.nextInChain = @ptrCast(&wgsl_src),
|
|
}) orelse return error.Shader;
|
|
defer c.wgpuShaderModuleRelease(shader);
|
|
|
|
return c.wgpuDeviceCreateComputePipeline(device, &.{
|
|
.compute = .{ .module = shader, .entryPoint = sv("main") },
|
|
}) orelse error.Pipeline;
|
|
}
|
|
|
|
fn sv(s: []const u8) c.WGPUStringView {
|
|
return .{ .data = s.ptr, .length = s.len };
|
|
}
|