From 545c4b98e95a2c0ccf2b0c6627495f9dd6f687d2 Mon Sep 17 00:00:00 2001 From: adrien Date: Wed, 20 May 2026 10:56:35 +0200 Subject: [PATCH] Create a GpuTextureView --- src/GpuAllocator.zig | 10 ++++++++++ src/GpuArena.zig | 36 +++++++++++++++++++++++++++++------- src/GpuBuffer.zig | 8 ++------ src/GpuDevice.zig | 5 +++++ src/GpuRender.zig | 5 +++-- src/GpuTexture.zig | 19 +++---------------- src/GpuTextureView.zig | 32 ++++++++++++++++++++++++++++++++ src/circle.zig | 7 ++++--- src/lib.zig | 11 +++++++++++ 9 files changed, 99 insertions(+), 34 deletions(-) create mode 100644 src/GpuTextureView.zig diff --git a/src/GpuAllocator.zig b/src/GpuAllocator.zig index ea98abd..517026e 100644 --- a/src/GpuAllocator.zig +++ b/src/GpuAllocator.zig @@ -6,6 +6,8 @@ pub const VTable = struct { freeBuffer: *const fn (ctx: *anyopaque, buf_raw: c.WGPUBuffer) void, allocTexture: *const fn (ctx: *anyopaque, desc: c.WGPUTextureDescriptor) anyerror!c.WGPUTexture, freeTexture: *const fn (ctx: *anyopaque, buf_raw: c.WGPUTexture) void, + allocTextureView: *const fn (ctx: *anyopaque, texture: c.WGPUTexture, desc: c.WGPUTextureViewDescriptor) anyerror!c.WGPUTextureView, + freeTextureView: *const fn (ctx: *anyopaque, buf_raw: c.WGPUTextureView) void, }; device: GpuDevice, @@ -27,3 +29,11 @@ pub fn allocTexture(self: @This(), desc: c.WGPUTextureDescriptor) !c.WGPUTexture pub fn freeTexture(self: @This(), buf_raw: c.WGPUTexture) void { self.vtable.freeTexture(self.ptr, buf_raw); } + +pub fn allocTextureView(self: @This(), texture: c.WGPUTexture, desc: c.WGPUTextureViewDescriptor) !c.WGPUTextureView { + return self.vtable.allocTextureView(self.ptr, texture, desc); +} + +pub fn freeTextureView(self: @This(), buf_raw: c.WGPUTextureView) void { + self.vtable.freeTextureView(self.ptr, buf_raw); +} diff --git a/src/GpuArena.zig b/src/GpuArena.zig index 10c7cf8..0353a8d 100644 --- a/src/GpuArena.zig +++ b/src/GpuArena.zig @@ -7,6 +7,7 @@ const c = @import("utils.zig").c; 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), allocated_vram_bytes: u64 = 0, pub fn init(cpu_allocator: std.mem.Allocator, device: GpuDevice) @This() { @@ -14,6 +15,7 @@ pub fn init(cpu_allocator: std.mem.Allocator, device: GpuDevice) @This() { .device = device, .tracked_buffers = .init(cpu_allocator), .tracked_textures = .init(cpu_allocator), + .tracked_views = .init(cpu_allocator), }; } @@ -29,6 +31,11 @@ pub fn deinit(self: *@This()) void { while (it_texture.next()) |tex_ptr| c.wgpuTextureRelease(tex_ptr.*); self.tracked_textures.deinit(); + + var it_view = self.tracked_views.keyIterator(); + while (it_view.next()) |view_ptr| + c.wgpuTextureViewRelease(view_ptr.*); + self.tracked_views.deinit(); } /// Returns the type-erased immutable interface wrapper @@ -41,6 +48,8 @@ pub fn gpuAllocator(self: *@This()) GpuAllocator { .freeBuffer = freeBuffer, .allocTexture = allocTexture, .freeTexture = freeTexture, + .allocTextureView = allocTextureView, + .freeTextureView = freeTextureView, }, }; } @@ -65,12 +74,12 @@ fn allocBuffer(ctx: *anyopaque, desc: c.WGPUBufferDescriptor) anyerror!c.WGPUBuf return buf; } -fn freeBuffer(ctx: *anyopaque, buf_raw: c.WGPUBuffer) void { +fn freeBuffer(ctx: *anyopaque, raw: c.WGPUBuffer) void { const self: *@This() = @ptrCast(@alignCast(ctx)); - if (self.tracked_buffers.fetchRemove(buf_raw)) |kv| { - c.wgpuBufferDestroy(buf_raw); - c.wgpuBufferRelease(buf_raw); + if (self.tracked_buffers.fetchRemove(raw)) |kv| { + c.wgpuBufferDestroy(raw); + c.wgpuBufferRelease(raw); self.allocated_vram_bytes -= kv.value.size; } } @@ -93,11 +102,11 @@ fn allocTexture(ctx: *anyopaque, desc: c.WGPUTextureDescriptor) anyerror!c.WGPUT return texture; } -fn freeTexture(ctx: *anyopaque, texture_raw: c.WGPUTexture) void { +fn freeTexture(ctx: *anyopaque, raw: c.WGPUTexture) void { const self: *@This() = @ptrCast(@alignCast(ctx)); - if (self.tracked_textures.fetchRemove(texture_raw)) |kv| { - c.wgpuTextureRelease(texture_raw); + if (self.tracked_textures.fetchRemove(raw)) |kv| { + c.wgpuTextureRelease(raw); const desc = kv.value; const format: GpuTextureFormat = @enumFromInt(desc.format); @@ -105,3 +114,16 @@ fn freeTexture(ctx: *anyopaque, texture_raw: c.WGPUTexture) void { self.allocated_vram_bytes -= bytes_size; } } + +fn allocTextureView(ctx: *anyopaque, texture: c.WGPUTexture, desc: c.WGPUTextureViewDescriptor) anyerror!c.WGPUTextureView { + const self: *@This() = @ptrCast(@alignCast(ctx)); + const view = c.wgpuTextureCreateView(texture, &desc) orelse return error.View; + try self.tracked_views.put(view, desc); + return view; +} + +fn freeTextureView(ctx: *anyopaque, raw: c.WGPUTextureView) void { + const self: *@This() = @ptrCast(@alignCast(ctx)); + if (self.tracked_views.remove(raw)) + c.wgpuTextureViewRelease(raw); +} diff --git a/src/GpuBuffer.zig b/src/GpuBuffer.zig index 099b83c..6632462 100644 --- a/src/GpuBuffer.zig +++ b/src/GpuBuffer.zig @@ -21,7 +21,6 @@ const BufferUsage = enum(u64) { QueryResolve = 0x0000000000000200, }; -/// Allocates the underlying WebGPU handle and registers it to the parent GpuAllocator pub fn init(gloc: GpuAllocator, size: u64, usage: std.EnumSet(BufferUsage)) !@This() { var use: u64 = 0; var iter = usage.iterator(); @@ -39,17 +38,14 @@ pub fn init(gloc: GpuAllocator, size: u64, usage: std.EnumSet(BufferUsage)) !@Th }; } -/// Unregisters from the parent GpuAllocator and cleanly destroys GPU resources pub fn deinit(self: @This()) void { self.gloc.freeBuffer(self.raw); } -/// Native getConstMappedRange wrapper pub fn getConstMappedRange(self: @This(), offset: u64, size: u64) ?*const anyopaque { return c.wgpuBufferGetConstMappedRange(self.raw, offset, size); } -/// Native mapAsync wrapper pub fn mapAsync( self: @This(), mode: c.WGPUMapMode, @@ -60,12 +56,11 @@ pub fn mapAsync( _ = c.wgpuBufferMapAsync(self.raw, mode, offset, size, callback_info); } -/// Native unmap wrapper pub fn unmap(self: @This()) void { c.wgpuBufferUnmap(self.raw); } -/// CPU to GPU. +/// CPU to GPU pub fn load( self: @This(), T: type, @@ -92,6 +87,7 @@ pub fn load( } } +/// GPU to CPU pub fn read(self: @This(), alloc: std.mem.Allocator, T: type) ![]T { const out = try alloc.alloc(T, @divExact(self.size, @sizeOf(T))); diff --git a/src/GpuDevice.zig b/src/GpuDevice.zig index 60c137f..7c4261b 100644 --- a/src/GpuDevice.zig +++ b/src/GpuDevice.zig @@ -2,6 +2,11 @@ const std = @import("std"); const c = @import("utils.zig").c; const sv = @import("utils.zig").sv; +// TODO: Make Allocator more zig like +// - GpuDevice can return a GpuAllocator that just allocate and nothing else +// - From this GpuAllocator, can create a GpuArena like std.heap.ArenaAllocator.init(allocator) +// - Rename GpuArenaAllocator too + const Ctx = struct { adapter: c.WGPUAdapter = null, device: c.WGPUDevice = null, diff --git a/src/GpuRender.zig b/src/GpuRender.zig index 53b3300..48d83b0 100644 --- a/src/GpuRender.zig +++ b/src/GpuRender.zig @@ -4,6 +4,7 @@ const sv = @import("utils.zig").sv; const GpuAllocator = @import("GpuAllocator.zig"); const GpuBuffer = @import("GpuBuffer.zig"); const GpuDevice = @import("GpuDevice.zig"); +const GpuTextureView = @import("GpuTextureView.zig"); const GpuTextureFormat = @import("lib.zig").GpuTextureFormat; pub const Binding = struct { @@ -99,7 +100,7 @@ pub fn deinit(self: @This()) void { pub fn draw( self: @This(), gloc: GpuAllocator, - target_view: c.WGPUTextureView, + target_view: GpuTextureView, vertex_count: u32, args: anytype, ) !void { @@ -144,7 +145,7 @@ pub fn draw( defer c.wgpuCommandEncoderRelease(enc); const color_attachment = c.WGPURenderPassColorAttachment{ - .view = target_view, + .view = target_view.raw, .resolveTarget = null, .loadOp = c.WGPULoadOp_Clear, .storeOp = c.WGPUStoreOp_Store, diff --git a/src/GpuTexture.zig b/src/GpuTexture.zig index fe7f7ed..7d75cb0 100644 --- a/src/GpuTexture.zig +++ b/src/GpuTexture.zig @@ -2,16 +2,7 @@ const std = @import("std"); const c = @import("utils.zig").c; const GpuAllocator = @import("GpuAllocator.zig"); const GpuTextureFormat = @import("lib.zig").GpuTextureFormat; - -const GpuTextureUsage = enum(u64) { - None = 0x0000000000000000, - CopySrc = 0x0000000000000001, - CopyDst = 0x0000000000000002, - TextureBinding = 0x0000000000000004, - StorageBinding = 0x0000000000000008, - RenderAttachment = 0x0000000000000010, - TransientAttachment = 0x0000000000000020, -}; +const GpuTextureUsage = @import("lib.zig").GpuTextureUsage; pub const GpuTextureDef = struct { size: c.WGPUExtent3D, @@ -23,7 +14,6 @@ raw: c.WGPUTexture, gloc: GpuAllocator, def: GpuTextureDef, -/// Allocates the underlying WebGPU handle and registers it to the parent GpuAllocator pub fn init(gloc: GpuAllocator, def: GpuTextureDef) !@This() { var use: u64 = 0; var iter = def.usage.iterator(); @@ -42,17 +32,14 @@ pub fn init(gloc: GpuAllocator, def: GpuTextureDef) !@This() { return .{ .gloc = gloc, .raw = raw, .def = def }; } -/// Unregisters from the parent GpuAllocator and cleanly destroys GPU resources pub fn deinit(self: @This()) void { self.gloc.freeTexture(self.raw); } -/// Native getConstMappedRange wrapper pub fn getConstMappedRange(self: @This(), offset: u64, size: u64) ?*const anyopaque { return c.wgpuBufferGetConstMappedRange(self.raw, offset, size); } -/// Native mapAsync wrapper pub fn mapAsync( self: @This(), mode: c.WGPUMapMode, @@ -63,12 +50,11 @@ pub fn mapAsync( _ = c.wgpuBufferMapAsync(self.raw, mode, offset, size, callback_info); } -/// Native unmap wrapper pub fn unmap(self: @This()) void { c.wgpuBufferUnmap(self.raw); } -/// CPU to GPU. +/// CPU to GPU pub fn load( self: @This(), T: type, @@ -95,6 +81,7 @@ pub fn load( } } +// GPU to CPU pub fn read(self: @This(), alloc: std.mem.Allocator, T: type) ![]T { const out = try alloc.alloc(T, @divExact(self.size, @sizeOf(T))); diff --git a/src/GpuTextureView.zig b/src/GpuTextureView.zig new file mode 100644 index 0000000..9475b9b --- /dev/null +++ b/src/GpuTextureView.zig @@ -0,0 +1,32 @@ +const std = @import("std"); +const c = @import("utils.zig").c; +const GpuAllocator = @import("GpuAllocator.zig"); +const GpuTexture = @import("lib.zig").GpuTexture; +const GpuTextureFormat = @import("lib.zig").GpuTextureFormat; +const GpuTextureUsage = @import("lib.zig").GpuTextureUsage; + +pub const GpuViewDef = struct { + usage: std.EnumSet(GpuTextureUsage) = .empty, + format: GpuTextureFormat = .Undefined, +}; + +raw: c.WGPUTextureView, +gloc: GpuAllocator, + +pub fn init(gloc: GpuAllocator, texture: GpuTexture, def: GpuViewDef) !@This() { + var use: u64 = 0; + var iter = def.usage.iterator(); + while (iter.next()) |flag| use |= @intFromEnum(flag); + + const raw = try gloc.allocTextureView(texture.raw, .{ + .format = @intFromEnum(def.format), + .usage = use, + .mipLevelCount = 1, + .arrayLayerCount = 1, + }); + return .{ .gloc = gloc, .raw = raw }; +} + +pub fn deinit(self: @This()) void { + self.gloc.freeTextureView(self.raw); +} diff --git a/src/circle.zig b/src/circle.zig index ee8e0ab..ef9ce7c 100644 --- a/src/circle.zig +++ b/src/circle.zig @@ -7,6 +7,7 @@ const GpuArena = gpu.GpuArena; const GpuBuffer = gpu.GpuBuffer; const GpuRender = gpu.GpuRender; const GpuTexture = gpu.GpuTexture; +const GpuTextureView = gpu.GpuTextureView; pub fn main(init: std.process.Init) !void { const allocator = init.gpa; @@ -42,8 +43,8 @@ pub fn main(init: std.process.Init) !void { }); defer texture.deinit(); - const target_view = c.wgpuTextureCreateView(texture.raw, null) orelse return error.View; - defer c.wgpuTextureViewRelease(target_view); + const view = try GpuTextureView.init(gloc, texture, .{}); + defer view.deinit(); // 4. Create a staging buffer to pull pixels from VRAM to CPU // 4 bytes per pixel (RGBA8) @@ -54,7 +55,7 @@ pub fn main(init: std.process.Init) !void { const cpu_staging_buf = try GpuBuffer.init(gloc, buffer_bytes, .initMany(&.{ .CopyDst, .CopySrc })); // 5. Draw the Circle Frame into the texture view! - try circle_rp.draw(gloc, target_view, 4, .{}); + try circle_rp.draw(gloc, view, 4, .{}); // 6. Copy the texture data into our CPU staging buffer const enc = c.wgpuDeviceCreateCommandEncoder(device.device, null) orelse return error.Encoder; diff --git a/src/lib.zig b/src/lib.zig index b7209a1..7525225 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -5,6 +5,7 @@ pub const GpuDevice = @import("GpuDevice.zig"); pub const GpuCompute = @import("GpuCompute.zig"); pub const GpuRender = @import("GpuRender.zig"); pub const GpuTexture = @import("GpuTexture.zig"); +pub const GpuTextureView = @import("GpuTextureView.zig"); pub const GpuTextureFormat = enum(c_uint) { Undefined = 0, @@ -174,3 +175,13 @@ pub const GpuTextureFormat = enum(c_uint) { }; } }; + +pub const GpuTextureUsage = enum(u64) { + None = 0x0000000000000000, + CopySrc = 0x0000000000000001, + CopyDst = 0x0000000000000002, + TextureBinding = 0x0000000000000004, + StorageBinding = 0x0000000000000008, + RenderAttachment = 0x0000000000000010, + TransientAttachment = 0x0000000000000020, +};