diff --git a/examples/bench_cp.zig b/examples/bench_cp.zig index fb0056c..3c506d0 100644 --- a/examples/bench_cp.zig +++ b/examples/bench_cp.zig @@ -1,7 +1,7 @@ const std = @import("std"); const gpu = @import("gpu"); const GpuDevice = gpu.GpuDevice; -const GpuArena = gpu.GpuArena; +const GpuArenaAllocator = gpu.GpuArenaAllocator; const GpuAllocator = gpu.GpuAllocator; const GpuBuffer = gpu.GpuBuffer; const GpuCompute = gpu.GpuCompute; @@ -60,9 +60,8 @@ pub fn main(init: std.process.Init) !void { const device = try GpuDevice.init(.{ .vram_bytes_limit = 4 * 1024 * 1024 * 1024 }); defer device.deinit(); - var grena = GpuArena.init(init.gpa, device); + var grena = GpuArenaAllocator.init(init.gpa, device.gpuAllocator()); defer grena.deinit(); - const gloc = grena.gpuAllocator(); const add_pip = try GpuCompute.init(gloc, @embedFile("shaders/add.wgsl"), .{ .bindings = &.{ diff --git a/examples/circle.zig b/examples/circle.zig index d3754e9..8fa63cd 100644 --- a/examples/circle.zig +++ b/examples/circle.zig @@ -1,7 +1,7 @@ const std = @import("std"); const gpu = @import("gpu"); const GpuDevice = gpu.GpuDevice; -const GpuArena = gpu.GpuArena; +const GpuArenaAllocator = gpu.GpuArenaAllocator; const GpuBuffer = gpu.GpuBuffer; const GpuRender = gpu.GpuRender; const GpuTexture = gpu.GpuTexture; @@ -22,7 +22,7 @@ pub fn main(init: std.process.Init) !void { defer device.deinit(); // 2. Init VRAM Arena - var grena = GpuArena.init(allocator, device); + var grena = GpuArenaAllocator.init(allocator, device.gpuAllocator()); defer grena.deinit(); const gloc = grena.gpuAllocator(); diff --git a/examples/compute.zig b/examples/compute.zig index 2c44697..4b9b939 100644 --- a/examples/compute.zig +++ b/examples/compute.zig @@ -1,7 +1,7 @@ const std = @import("std"); const gpu = @import("gpu"); const GpuDevice = gpu.GpuDevice; -const GpuArena = gpu.GpuArena; +const GpuArenaAllocator = gpu.GpuArenaAllocator; const GpuBuffer = gpu.GpuBuffer; const GpuCompute = gpu.GpuCompute; @@ -13,7 +13,7 @@ pub fn main(init: std.process.Init) !void { defer device.deinit(); // 2. Create a GPU Arena to manage VRAM - var grena = GpuArena.init(allocator, device); + var grena = GpuArenaAllocator.init(allocator, device.gpuAllocator()); defer grena.deinit(); const gloc = grena.gpuAllocator(); diff --git a/src/GpuArena.zig b/src/GpuArenaAllocator.zig similarity index 78% rename from src/GpuArena.zig rename to src/GpuArenaAllocator.zig index 7644caa..36da463 100644 --- a/src/GpuArena.zig +++ b/src/GpuArenaAllocator.zig @@ -4,7 +4,7 @@ const GpuAllocator = @import("GpuAllocator.zig"); const GpuTextureFormat = @import("lib.zig").GpuTextureFormat; const c = @import("utils.zig").c; -device: GpuDevice, +child_allocator: GpuAllocator, // I use Zig naming child_allocator, but that should be a parent for me. Likely something idk tracked_buffers: std.AutoHashMap(c.WGPUBuffer, c.WGPUBufferDescriptor), tracked_textures: std.AutoHashMap(c.WGPUTexture, c.WGPUTextureDescriptor), tracked_views: std.AutoHashMap(c.WGPUTextureView, c.WGPUTextureViewDescriptor), @@ -12,9 +12,9 @@ tracked_renders: std.AutoHashMap(c.WGPURenderPipeline, c.WGPURenderPipelineDescr tracked_computes: std.AutoHashMap(c.WGPUComputePipeline, c.WGPUComputePipelineDescriptor), allocated_vram_bytes: u64 = 0, -pub fn init(cpu_allocator: std.mem.Allocator, device: GpuDevice) @This() { +pub fn init(cpu_allocator: std.mem.Allocator, child_allocator: GpuAllocator) @This() { return .{ - .device = device, + .child_allocator = child_allocator, .tracked_buffers = .init(cpu_allocator), .tracked_textures = .init(cpu_allocator), .tracked_views = .init(cpu_allocator), @@ -24,38 +24,36 @@ pub fn init(cpu_allocator: std.mem.Allocator, device: GpuDevice) @This() { } pub fn deinit(self: *@This()) void { - const gloc = self.gpuAllocator(); - var it_buffer = self.tracked_buffers.keyIterator(); while (it_buffer.next()) |buf_ptr| - gloc.freeBuffer(buf_ptr.*); + self.child_allocator.freeBuffer(buf_ptr.*); self.tracked_buffers.deinit(); var it_tex = self.tracked_textures.keyIterator(); while (it_tex.next()) |buf_ptr| - gloc.freeTexture(buf_ptr.*); + self.child_allocator.freeTexture(buf_ptr.*); self.tracked_textures.deinit(); var it_view = self.tracked_views.keyIterator(); while (it_view.next()) |buf_ptr| - gloc.freeTextureView(buf_ptr.*); + self.child_allocator.freeTextureView(buf_ptr.*); self.tracked_views.deinit(); var it_render = self.tracked_renders.keyIterator(); while (it_render.next()) |buf_ptr| - gloc.freeRenderPipeline(buf_ptr.*); + self.child_allocator.freeRenderPipeline(buf_ptr.*); self.tracked_renders.deinit(); var it_compute = self.tracked_computes.keyIterator(); while (it_compute.next()) |buf_ptr| - gloc.freeComputePipeline(buf_ptr.*); + self.child_allocator.freeComputePipeline(buf_ptr.*); self.tracked_computes.deinit(); } /// Returns the type-erased immutable interface wrapper pub fn gpuAllocator(self: *@This()) GpuAllocator { return .{ - .device = self.device, + .device = self.child_allocator.device, .ptr = self, .vtable = &.{ .allocBuffer = allocBuffer, @@ -78,15 +76,7 @@ pub fn gpuAllocator(self: *@This()) GpuAllocator { fn allocBuffer(ctx: *anyopaque, desc: c.WGPUBufferDescriptor) anyerror!c.WGPUBuffer { const self: *@This() = @ptrCast(@alignCast(ctx)); try self.tracked_buffers.ensureTotalCapacity(self.tracked_buffers.count() + 1); - - if (desc.size > self.device.limits.maxBufferSize) - return error.SingleBufferExceedsLimit; - - if (desc.size + self.allocated_vram_bytes > self.device.config.vram_bytes_limit) - return error.ExceedsVramBudget; - - const raw = c.wgpuDeviceCreateBuffer(self.device.device, &desc) orelse return error.BufferAlloc; - + const raw = try self.child_allocator.allocBuffer(desc); self.tracked_buffers.putAssumeCapacity(raw, desc); self.allocated_vram_bytes += desc.size; return raw; @@ -94,10 +84,8 @@ fn allocBuffer(ctx: *anyopaque, desc: c.WGPUBufferDescriptor) anyerror!c.WGPUBuf fn freeBuffer(ctx: *anyopaque, raw: c.WGPUBuffer) void { const self: *@This() = @ptrCast(@alignCast(ctx)); - if (self.tracked_buffers.fetchRemove(raw)) |kv| { - c.wgpuBufferDestroy(raw); - c.wgpuBufferRelease(raw); + self.child_allocator.freeBuffer(raw); self.allocated_vram_bytes -= kv.value.size; } } @@ -108,13 +96,11 @@ fn allocTexture(ctx: *anyopaque, desc: c.WGPUTextureDescriptor) anyerror!c.WGPUT const format: GpuTextureFormat = @enumFromInt(desc.format); const bytes_size = desc.size.width * desc.size.height * format.bytesPerPixel(); - if (bytes_size > self.device.limits.maxBufferSize) - return error.SingleBufferExceedsLimit; - if (bytes_size + self.allocated_vram_bytes > self.device.config.vram_bytes_limit) + if (bytes_size + self.allocated_vram_bytes > self.child_allocator.device.config.vram_bytes_limit) return error.ExceedsVramBudget; - const raw = c.wgpuDeviceCreateTexture(self.device.device, &desc) orelse return error.Texture; + const raw = try self.child_allocator.allocTexture(desc); self.tracked_textures.putAssumeCapacity(raw, desc); self.allocated_vram_bytes += bytes_size; @@ -125,7 +111,7 @@ fn freeTexture(ctx: *anyopaque, raw: c.WGPUTexture) void { const self: *@This() = @ptrCast(@alignCast(ctx)); if (self.tracked_textures.fetchRemove(raw)) |kv| { - c.wgpuTextureRelease(raw); + self.child_allocator.freeTexture(raw); const desc = kv.value; const format: GpuTextureFormat = @enumFromInt(desc.format); @@ -137,7 +123,7 @@ fn freeTexture(ctx: *anyopaque, raw: c.WGPUTexture) void { fn allocTextureView(ctx: *anyopaque, texture: c.WGPUTexture, desc: c.WGPUTextureViewDescriptor) anyerror!c.WGPUTextureView { const self: *@This() = @ptrCast(@alignCast(ctx)); try self.tracked_views.ensureTotalCapacity(self.tracked_views.count() + 1); - const raw = c.wgpuTextureCreateView(texture, &desc) orelse return error.View; + const raw = try self.child_allocator.allocTextureView(texture, desc); self.tracked_views.putAssumeCapacity(raw, desc); return raw; } @@ -145,13 +131,13 @@ fn allocTextureView(ctx: *anyopaque, texture: c.WGPUTexture, desc: c.WGPUTexture fn freeTextureView(ctx: *anyopaque, raw: c.WGPUTextureView) void { const self: *@This() = @ptrCast(@alignCast(ctx)); if (self.tracked_views.remove(raw)) - c.wgpuTextureViewRelease(raw); + self.child_allocator.freeTextureView(raw); } fn allocRenderPipeline(ctx: *anyopaque, desc: c.WGPURenderPipelineDescriptor) anyerror!c.WGPURenderPipeline { const self: *@This() = @ptrCast(@alignCast(ctx)); try self.tracked_renders.ensureTotalCapacity(self.tracked_renders.count() + 1); - const raw = c.wgpuDeviceCreateRenderPipeline(self.device.device, &desc) orelse return error.Pipeline; + const raw = try self.child_allocator.allocRenderPipeline(desc); self.tracked_renders.putAssumeCapacity(raw, desc); return raw; } @@ -159,13 +145,13 @@ fn allocRenderPipeline(ctx: *anyopaque, desc: c.WGPURenderPipelineDescriptor) an fn freeRenderPipeline(ctx: *anyopaque, raw: c.WGPURenderPipeline) void { const self: *@This() = @ptrCast(@alignCast(ctx)); if (self.tracked_renders.remove(raw)) - c.wgpuRenderPipelineRelease(raw); + self.child_allocator.freeRenderPipeline(raw); } fn allocComputePipeline(ctx: *anyopaque, desc: c.WGPUComputePipelineDescriptor) anyerror!c.WGPUComputePipeline { const self: *@This() = @ptrCast(@alignCast(ctx)); try self.tracked_computes.ensureTotalCapacity(self.tracked_computes.count() + 1); - const raw = c.wgpuDeviceCreateComputePipeline(self.device.device, &desc) orelse return error.Pipeline; + const raw = try self.child_allocator.allocComputePipeline(desc); self.tracked_computes.putAssumeCapacity(raw, desc); return raw; } @@ -173,5 +159,5 @@ fn allocComputePipeline(ctx: *anyopaque, desc: c.WGPUComputePipelineDescriptor) fn freeComputePipeline(ctx: *anyopaque, raw: c.WGPUComputePipeline) void { const self: *@This() = @ptrCast(@alignCast(ctx)); if (self.tracked_computes.remove(raw)) - c.wgpuComputePipelineRelease(raw); + self.child_allocator.freeComputePipeline(raw); } diff --git a/src/GpuDevice.zig b/src/GpuDevice.zig index 7c4261b..8463aa2 100644 --- a/src/GpuDevice.zig +++ b/src/GpuDevice.zig @@ -1,6 +1,8 @@ const std = @import("std"); const c = @import("utils.zig").c; const sv = @import("utils.zig").sv; +const GpuAllocator = @import("GpuAllocator.zig"); +const GpuTextureFormat = @import("lib.zig").GpuTextureFormat; // TODO: Make Allocator more zig like // - GpuDevice can return a GpuAllocator that just allocate and nothing else @@ -132,3 +134,75 @@ fn onDevice( const ctx: *Ctx = @ptrCast(@alignCast(userdata1.?)); ctx.device = device; } + +// Allocation stuff + +/// Returns the type-erased immutable interface wrapper +pub fn gpuAllocator(self: *const @This()) GpuAllocator { + return .{ + .device = self.*, + .ptr = @ptrCast(@constCast(self)), + .vtable = &.{ + .allocBuffer = allocBuffer, + .freeBuffer = freeBuffer, + .allocTexture = allocTexture, + .freeTexture = freeTexture, + .allocTextureView = allocTextureView, + .freeTextureView = freeTextureView, + .allocRenderPipeline = allocRenderPipeline, + .freeRenderPipeline = freeRenderPipeline, + .allocComputePipeline = allocComputePipeline, + .freeComputePipeline = freeComputePipeline, + }, + }; +} + +fn allocBuffer(ctx: *anyopaque, desc: c.WGPUBufferDescriptor) anyerror!c.WGPUBuffer { + const self: *@This() = @ptrCast(@alignCast(ctx)); + if (desc.size > self.limits.maxBufferSize) + return error.SingleBufferExceedsLimit; + return c.wgpuDeviceCreateBuffer(self.device, &desc) orelse return error.BufferAlloc; +} + +fn freeBuffer(_: *anyopaque, raw: c.WGPUBuffer) void { + c.wgpuBufferDestroy(raw); + c.wgpuBufferRelease(raw); +} + +fn allocTexture(ctx: *anyopaque, desc: c.WGPUTextureDescriptor) anyerror!c.WGPUTexture { + const self: *@This() = @ptrCast(@alignCast(ctx)); + const format: GpuTextureFormat = @enumFromInt(desc.format); + if (desc.size.width * desc.size.height * format.bytesPerPixel() > self.limits.maxBufferSize) + return error.SingleBufferExceedsLimit; + return c.wgpuDeviceCreateTexture(self.device, &desc) orelse return error.Texture; +} + +fn freeTexture(_: *anyopaque, raw: c.WGPUTexture) void { + c.wgpuTextureRelease(raw); +} + +fn allocTextureView(_: *anyopaque, texture: c.WGPUTexture, desc: c.WGPUTextureViewDescriptor) anyerror!c.WGPUTextureView { + return c.wgpuTextureCreateView(texture, &desc) orelse return error.View; +} + +fn freeTextureView(_: *anyopaque, raw: c.WGPUTextureView) void { + c.wgpuTextureViewRelease(raw); +} + +fn allocRenderPipeline(ctx: *anyopaque, desc: c.WGPURenderPipelineDescriptor) anyerror!c.WGPURenderPipeline { + const self: *@This() = @ptrCast(@alignCast(ctx)); + return c.wgpuDeviceCreateRenderPipeline(self.device, &desc) orelse return error.Pipeline; +} + +fn freeRenderPipeline(_: *anyopaque, raw: c.WGPURenderPipeline) void { + c.wgpuRenderPipelineRelease(raw); +} + +fn allocComputePipeline(ctx: *anyopaque, desc: c.WGPUComputePipelineDescriptor) anyerror!c.WGPUComputePipeline { + const self: *@This() = @ptrCast(@alignCast(ctx)); + return c.wgpuDeviceCreateComputePipeline(self.device, &desc) orelse return error.Pipeline; +} + +fn freeComputePipeline(_: *anyopaque, raw: c.WGPUComputePipeline) void { + c.wgpuComputePipelineRelease(raw); +} diff --git a/src/lib.zig b/src/lib.zig index 7525225..1ba633e 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -1,5 +1,5 @@ pub const GpuAllocator = @import("GpuAllocator.zig"); -pub const GpuArena = @import("GpuArena.zig"); +pub const GpuArenaAllocator = @import("GpuArenaAllocator.zig"); pub const GpuBuffer = @import("GpuBuffer.zig"); pub const GpuDevice = @import("GpuDevice.zig"); pub const GpuCompute = @import("GpuCompute.zig");