Compare commits

..

No commits in common. "5104d61ef612e19f4cef2ebb6823eb5e5bd6664f" and "a06b040a298994d7739a463465e302847f69ebc8" have entirely different histories.

7 changed files with 54 additions and 104 deletions

4
circle.ppm Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,7 @@
const std = @import("std");
const gpu = @import("gpu");
const GpuDevice = gpu.GpuDevice;
const GpuArenaAllocator = gpu.GpuArenaAllocator;
const GpuArena = gpu.GpuArena;
const GpuAllocator = gpu.GpuAllocator;
const GpuBuffer = gpu.GpuBuffer;
const GpuCompute = gpu.GpuCompute;
@ -60,8 +60,9 @@ 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 = GpuArenaAllocator.init(init.gpa, device.gpuAllocator());
var grena = GpuArena.init(init.gpa, device);
defer grena.deinit();
const gloc = grena.gpuAllocator();
const add_pip = try GpuCompute.init(gloc, @embedFile("shaders/add.wgsl"), .{ .bindings = &.{

View File

@ -1,7 +1,7 @@
const std = @import("std");
const gpu = @import("gpu");
const GpuDevice = gpu.GpuDevice;
const GpuArenaAllocator = gpu.GpuArenaAllocator;
const GpuArena = gpu.GpuArena;
const GpuBuffer = gpu.GpuBuffer;
const GpuRender = gpu.GpuRender;
const GpuTexture = gpu.GpuTexture;
@ -10,6 +10,10 @@ const GpuTextureView = gpu.GpuTextureView;
const width: u32 = 512;
const height: u32 = 512;
// Note: Everything using a GpuAllocator in init from an GpuAllocatorArena is safely
// tied to it which will automatically release them when deinit the arena itself.
// You can also manually call x.deinit() if desired.
pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
@ -18,7 +22,9 @@ pub fn main(init: std.process.Init) !void {
defer device.deinit();
// 2. Init VRAM Arena
const gloc = device.gpuAllocator();
var grena = GpuArena.init(allocator, device);
defer grena.deinit();
const gloc = grena.gpuAllocator();
// 3. Load Render Pipeline
const circle_rp = try GpuRender.init(
@ -48,7 +54,6 @@ pub fn main(init: std.process.Init) !void {
defer cpu_staging_cpu.deinit();
// 8. Read GpuBuffer to CPU
// This need to be free manually because CPU memory
const pixels = try cpu_staging_cpu.read(allocator, u8);
defer allocator.free(pixels);

View File

@ -1,7 +1,7 @@
const std = @import("std");
const gpu = @import("gpu");
const GpuDevice = gpu.GpuDevice;
const GpuArenaAllocator = gpu.GpuArenaAllocator;
const GpuArena = gpu.GpuArena;
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 = GpuArenaAllocator.init(allocator, device.gpuAllocator());
var grena = GpuArena.init(allocator, device);
defer grena.deinit();
const gloc = grena.gpuAllocator();
@ -27,6 +27,7 @@ pub fn main(init: std.process.Init) !void {
.{ .element_size = @sizeOf(f16) },
} },
);
defer add_cp.deinit();
// 4. Setup CPU data
const len: usize = 16;
@ -46,9 +47,8 @@ pub fn main(init: std.process.Init) !void {
const buf_b = try GpuBuffer.init(gloc, byte_size, .initMany(&.{ .Storage, .CopyDst, .CopySrc }));
const buf_out = try GpuBuffer.init(gloc, byte_size, .initMany(&.{ .Storage, .CopyDst, .CopySrc }));
// Note: Buffers are safely tied to the GpuArenaAllocator which will automatically
// Note: The buffers are safely tied to the GpuArena which will automatically
// release them at the end. You can also manually call buf_x.deinit() if desired.
// This will also release pipelines, textures, ect. Everything using a GpuAllocator to init.
// 6. Transfer data from CPU slices to GPU Buffers
try buf_a.load(f16, data_a);

View File

@ -4,7 +4,7 @@ const GpuAllocator = @import("GpuAllocator.zig");
const GpuTextureFormat = @import("lib.zig").GpuTextureFormat;
const c = @import("utils.zig").c;
child_allocator: GpuAllocator, // I use Zig naming child_allocator, but that should be a parent for me. Likely something idk
device: GpuDevice,
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, child_allocator: GpuAllocator) @This() {
pub fn init(cpu_allocator: std.mem.Allocator, device: GpuDevice) @This() {
return .{
.child_allocator = child_allocator,
.device = device,
.tracked_buffers = .init(cpu_allocator),
.tracked_textures = .init(cpu_allocator),
.tracked_views = .init(cpu_allocator),
@ -24,36 +24,38 @@ pub fn init(cpu_allocator: std.mem.Allocator, child_allocator: GpuAllocator) @Th
}
pub fn deinit(self: *@This()) void {
const gloc = self.gpuAllocator();
var it_buffer = self.tracked_buffers.keyIterator();
while (it_buffer.next()) |buf_ptr|
self.child_allocator.freeBuffer(buf_ptr.*);
gloc.freeBuffer(buf_ptr.*);
self.tracked_buffers.deinit();
var it_tex = self.tracked_textures.keyIterator();
while (it_tex.next()) |buf_ptr|
self.child_allocator.freeTexture(buf_ptr.*);
gloc.freeTexture(buf_ptr.*);
self.tracked_textures.deinit();
var it_view = self.tracked_views.keyIterator();
while (it_view.next()) |buf_ptr|
self.child_allocator.freeTextureView(buf_ptr.*);
gloc.freeTextureView(buf_ptr.*);
self.tracked_views.deinit();
var it_render = self.tracked_renders.keyIterator();
while (it_render.next()) |buf_ptr|
self.child_allocator.freeRenderPipeline(buf_ptr.*);
gloc.freeRenderPipeline(buf_ptr.*);
self.tracked_renders.deinit();
var it_compute = self.tracked_computes.keyIterator();
while (it_compute.next()) |buf_ptr|
self.child_allocator.freeComputePipeline(buf_ptr.*);
gloc.freeComputePipeline(buf_ptr.*);
self.tracked_computes.deinit();
}
/// Returns the type-erased immutable interface wrapper
pub fn gpuAllocator(self: *@This()) GpuAllocator {
return .{
.device = self.child_allocator.device,
.device = self.device,
.ptr = self,
.vtable = &.{
.allocBuffer = allocBuffer,
@ -76,7 +78,15 @@ 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);
const raw = try self.child_allocator.allocBuffer(desc);
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;
self.tracked_buffers.putAssumeCapacity(raw, desc);
self.allocated_vram_bytes += desc.size;
return raw;
@ -84,8 +94,10 @@ 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| {
self.child_allocator.freeBuffer(raw);
c.wgpuBufferDestroy(raw);
c.wgpuBufferRelease(raw);
self.allocated_vram_bytes -= kv.value.size;
}
}
@ -96,11 +108,13 @@ 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.child_allocator.device.config.vram_bytes_limit)
if (bytes_size + self.allocated_vram_bytes > self.device.config.vram_bytes_limit)
return error.ExceedsVramBudget;
const raw = try self.child_allocator.allocTexture(desc);
const raw = c.wgpuDeviceCreateTexture(self.device.device, &desc) orelse return error.Texture;
self.tracked_textures.putAssumeCapacity(raw, desc);
self.allocated_vram_bytes += bytes_size;
@ -111,7 +125,7 @@ fn freeTexture(ctx: *anyopaque, raw: c.WGPUTexture) void {
const self: *@This() = @ptrCast(@alignCast(ctx));
if (self.tracked_textures.fetchRemove(raw)) |kv| {
self.child_allocator.freeTexture(raw);
c.wgpuTextureRelease(raw);
const desc = kv.value;
const format: GpuTextureFormat = @enumFromInt(desc.format);
@ -123,7 +137,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 = try self.child_allocator.allocTextureView(texture, desc);
const raw = c.wgpuTextureCreateView(texture, &desc) orelse return error.View;
self.tracked_views.putAssumeCapacity(raw, desc);
return raw;
}
@ -131,13 +145,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))
self.child_allocator.freeTextureView(raw);
c.wgpuTextureViewRelease(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 = try self.child_allocator.allocRenderPipeline(desc);
const raw = c.wgpuDeviceCreateRenderPipeline(self.device.device, &desc) orelse return error.Pipeline;
self.tracked_renders.putAssumeCapacity(raw, desc);
return raw;
}
@ -145,13 +159,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))
self.child_allocator.freeRenderPipeline(raw);
c.wgpuRenderPipelineRelease(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 = try self.child_allocator.allocComputePipeline(desc);
const raw = c.wgpuDeviceCreateComputePipeline(self.device.device, &desc) orelse return error.Pipeline;
self.tracked_computes.putAssumeCapacity(raw, desc);
return raw;
}
@ -159,5 +173,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))
self.child_allocator.freeComputePipeline(raw);
c.wgpuComputePipelineRelease(raw);
}

View File

@ -1,8 +1,6 @@
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
@ -134,75 +132,3 @@ 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);
}

View File

@ -1,5 +1,5 @@
pub const GpuAllocator = @import("GpuAllocator.zig");
pub const GpuArenaAllocator = @import("GpuArenaAllocator.zig");
pub const GpuArena = @import("GpuArena.zig");
pub const GpuBuffer = @import("GpuBuffer.zig");
pub const GpuDevice = @import("GpuDevice.zig");
pub const GpuCompute = @import("GpuCompute.zig");