Create a GpuTextureView

This commit is contained in:
adrien 2026-05-20 10:56:35 +02:00
parent 45c0f3180e
commit 545c4b98e9
9 changed files with 99 additions and 34 deletions

View File

@ -6,6 +6,8 @@ pub const VTable = struct {
freeBuffer: *const fn (ctx: *anyopaque, buf_raw: c.WGPUBuffer) void, freeBuffer: *const fn (ctx: *anyopaque, buf_raw: c.WGPUBuffer) void,
allocTexture: *const fn (ctx: *anyopaque, desc: c.WGPUTextureDescriptor) anyerror!c.WGPUTexture, allocTexture: *const fn (ctx: *anyopaque, desc: c.WGPUTextureDescriptor) anyerror!c.WGPUTexture,
freeTexture: *const fn (ctx: *anyopaque, buf_raw: c.WGPUTexture) void, 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, 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 { pub fn freeTexture(self: @This(), buf_raw: c.WGPUTexture) void {
self.vtable.freeTexture(self.ptr, buf_raw); 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);
}

View File

@ -7,6 +7,7 @@ const c = @import("utils.zig").c;
device: GpuDevice, device: GpuDevice,
tracked_buffers: std.AutoHashMap(c.WGPUBuffer, c.WGPUBufferDescriptor), tracked_buffers: std.AutoHashMap(c.WGPUBuffer, c.WGPUBufferDescriptor),
tracked_textures: std.AutoHashMap(c.WGPUTexture, c.WGPUTextureDescriptor), tracked_textures: std.AutoHashMap(c.WGPUTexture, c.WGPUTextureDescriptor),
tracked_views: std.AutoHashMap(c.WGPUTextureView, c.WGPUTextureViewDescriptor),
allocated_vram_bytes: u64 = 0, allocated_vram_bytes: u64 = 0,
pub fn init(cpu_allocator: std.mem.Allocator, device: GpuDevice) @This() { 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, .device = device,
.tracked_buffers = .init(cpu_allocator), .tracked_buffers = .init(cpu_allocator),
.tracked_textures = .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| while (it_texture.next()) |tex_ptr|
c.wgpuTextureRelease(tex_ptr.*); c.wgpuTextureRelease(tex_ptr.*);
self.tracked_textures.deinit(); 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 /// Returns the type-erased immutable interface wrapper
@ -41,6 +48,8 @@ pub fn gpuAllocator(self: *@This()) GpuAllocator {
.freeBuffer = freeBuffer, .freeBuffer = freeBuffer,
.allocTexture = allocTexture, .allocTexture = allocTexture,
.freeTexture = freeTexture, .freeTexture = freeTexture,
.allocTextureView = allocTextureView,
.freeTextureView = freeTextureView,
}, },
}; };
} }
@ -65,12 +74,12 @@ fn allocBuffer(ctx: *anyopaque, desc: c.WGPUBufferDescriptor) anyerror!c.WGPUBuf
return buf; 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)); const self: *@This() = @ptrCast(@alignCast(ctx));
if (self.tracked_buffers.fetchRemove(buf_raw)) |kv| { if (self.tracked_buffers.fetchRemove(raw)) |kv| {
c.wgpuBufferDestroy(buf_raw); c.wgpuBufferDestroy(raw);
c.wgpuBufferRelease(buf_raw); c.wgpuBufferRelease(raw);
self.allocated_vram_bytes -= kv.value.size; self.allocated_vram_bytes -= kv.value.size;
} }
} }
@ -93,11 +102,11 @@ fn allocTexture(ctx: *anyopaque, desc: c.WGPUTextureDescriptor) anyerror!c.WGPUT
return texture; 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)); const self: *@This() = @ptrCast(@alignCast(ctx));
if (self.tracked_textures.fetchRemove(texture_raw)) |kv| { if (self.tracked_textures.fetchRemove(raw)) |kv| {
c.wgpuTextureRelease(texture_raw); c.wgpuTextureRelease(raw);
const desc = kv.value; const desc = kv.value;
const format: GpuTextureFormat = @enumFromInt(desc.format); 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; 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);
}

View File

@ -21,7 +21,6 @@ const BufferUsage = enum(u64) {
QueryResolve = 0x0000000000000200, 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() { pub fn init(gloc: GpuAllocator, size: u64, usage: std.EnumSet(BufferUsage)) !@This() {
var use: u64 = 0; var use: u64 = 0;
var iter = usage.iterator(); 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 { pub fn deinit(self: @This()) void {
self.gloc.freeBuffer(self.raw); self.gloc.freeBuffer(self.raw);
} }
/// Native getConstMappedRange wrapper
pub fn getConstMappedRange(self: @This(), offset: u64, size: u64) ?*const anyopaque { pub fn getConstMappedRange(self: @This(), offset: u64, size: u64) ?*const anyopaque {
return c.wgpuBufferGetConstMappedRange(self.raw, offset, size); return c.wgpuBufferGetConstMappedRange(self.raw, offset, size);
} }
/// Native mapAsync wrapper
pub fn mapAsync( pub fn mapAsync(
self: @This(), self: @This(),
mode: c.WGPUMapMode, mode: c.WGPUMapMode,
@ -60,12 +56,11 @@ pub fn mapAsync(
_ = c.wgpuBufferMapAsync(self.raw, mode, offset, size, callback_info); _ = c.wgpuBufferMapAsync(self.raw, mode, offset, size, callback_info);
} }
/// Native unmap wrapper
pub fn unmap(self: @This()) void { pub fn unmap(self: @This()) void {
c.wgpuBufferUnmap(self.raw); c.wgpuBufferUnmap(self.raw);
} }
/// CPU to GPU. /// CPU to GPU
pub fn load( pub fn load(
self: @This(), self: @This(),
T: type, T: type,
@ -92,6 +87,7 @@ pub fn load(
} }
} }
/// GPU to CPU
pub fn read(self: @This(), alloc: std.mem.Allocator, T: type) ![]T { pub fn read(self: @This(), alloc: std.mem.Allocator, T: type) ![]T {
const out = try alloc.alloc(T, @divExact(self.size, @sizeOf(T))); const out = try alloc.alloc(T, @divExact(self.size, @sizeOf(T)));

View File

@ -2,6 +2,11 @@ const std = @import("std");
const c = @import("utils.zig").c; const c = @import("utils.zig").c;
const sv = @import("utils.zig").sv; 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 { const Ctx = struct {
adapter: c.WGPUAdapter = null, adapter: c.WGPUAdapter = null,
device: c.WGPUDevice = null, device: c.WGPUDevice = null,

View File

@ -4,6 +4,7 @@ const sv = @import("utils.zig").sv;
const GpuAllocator = @import("GpuAllocator.zig"); const GpuAllocator = @import("GpuAllocator.zig");
const GpuBuffer = @import("GpuBuffer.zig"); const GpuBuffer = @import("GpuBuffer.zig");
const GpuDevice = @import("GpuDevice.zig"); const GpuDevice = @import("GpuDevice.zig");
const GpuTextureView = @import("GpuTextureView.zig");
const GpuTextureFormat = @import("lib.zig").GpuTextureFormat; const GpuTextureFormat = @import("lib.zig").GpuTextureFormat;
pub const Binding = struct { pub const Binding = struct {
@ -99,7 +100,7 @@ pub fn deinit(self: @This()) void {
pub fn draw( pub fn draw(
self: @This(), self: @This(),
gloc: GpuAllocator, gloc: GpuAllocator,
target_view: c.WGPUTextureView, target_view: GpuTextureView,
vertex_count: u32, vertex_count: u32,
args: anytype, args: anytype,
) !void { ) !void {
@ -144,7 +145,7 @@ pub fn draw(
defer c.wgpuCommandEncoderRelease(enc); defer c.wgpuCommandEncoderRelease(enc);
const color_attachment = c.WGPURenderPassColorAttachment{ const color_attachment = c.WGPURenderPassColorAttachment{
.view = target_view, .view = target_view.raw,
.resolveTarget = null, .resolveTarget = null,
.loadOp = c.WGPULoadOp_Clear, .loadOp = c.WGPULoadOp_Clear,
.storeOp = c.WGPUStoreOp_Store, .storeOp = c.WGPUStoreOp_Store,

View File

@ -2,16 +2,7 @@ const std = @import("std");
const c = @import("utils.zig").c; const c = @import("utils.zig").c;
const GpuAllocator = @import("GpuAllocator.zig"); const GpuAllocator = @import("GpuAllocator.zig");
const GpuTextureFormat = @import("lib.zig").GpuTextureFormat; const GpuTextureFormat = @import("lib.zig").GpuTextureFormat;
const GpuTextureUsage = @import("lib.zig").GpuTextureUsage;
const GpuTextureUsage = enum(u64) {
None = 0x0000000000000000,
CopySrc = 0x0000000000000001,
CopyDst = 0x0000000000000002,
TextureBinding = 0x0000000000000004,
StorageBinding = 0x0000000000000008,
RenderAttachment = 0x0000000000000010,
TransientAttachment = 0x0000000000000020,
};
pub const GpuTextureDef = struct { pub const GpuTextureDef = struct {
size: c.WGPUExtent3D, size: c.WGPUExtent3D,
@ -23,7 +14,6 @@ raw: c.WGPUTexture,
gloc: GpuAllocator, gloc: GpuAllocator,
def: GpuTextureDef, def: GpuTextureDef,
/// Allocates the underlying WebGPU handle and registers it to the parent GpuAllocator
pub fn init(gloc: GpuAllocator, def: GpuTextureDef) !@This() { pub fn init(gloc: GpuAllocator, def: GpuTextureDef) !@This() {
var use: u64 = 0; var use: u64 = 0;
var iter = def.usage.iterator(); var iter = def.usage.iterator();
@ -42,17 +32,14 @@ pub fn init(gloc: GpuAllocator, def: GpuTextureDef) !@This() {
return .{ .gloc = gloc, .raw = raw, .def = def }; return .{ .gloc = gloc, .raw = raw, .def = def };
} }
/// Unregisters from the parent GpuAllocator and cleanly destroys GPU resources
pub fn deinit(self: @This()) void { pub fn deinit(self: @This()) void {
self.gloc.freeTexture(self.raw); self.gloc.freeTexture(self.raw);
} }
/// Native getConstMappedRange wrapper
pub fn getConstMappedRange(self: @This(), offset: u64, size: u64) ?*const anyopaque { pub fn getConstMappedRange(self: @This(), offset: u64, size: u64) ?*const anyopaque {
return c.wgpuBufferGetConstMappedRange(self.raw, offset, size); return c.wgpuBufferGetConstMappedRange(self.raw, offset, size);
} }
/// Native mapAsync wrapper
pub fn mapAsync( pub fn mapAsync(
self: @This(), self: @This(),
mode: c.WGPUMapMode, mode: c.WGPUMapMode,
@ -63,12 +50,11 @@ pub fn mapAsync(
_ = c.wgpuBufferMapAsync(self.raw, mode, offset, size, callback_info); _ = c.wgpuBufferMapAsync(self.raw, mode, offset, size, callback_info);
} }
/// Native unmap wrapper
pub fn unmap(self: @This()) void { pub fn unmap(self: @This()) void {
c.wgpuBufferUnmap(self.raw); c.wgpuBufferUnmap(self.raw);
} }
/// CPU to GPU. /// CPU to GPU
pub fn load( pub fn load(
self: @This(), self: @This(),
T: type, T: type,
@ -95,6 +81,7 @@ pub fn load(
} }
} }
// GPU to CPU
pub fn read(self: @This(), alloc: std.mem.Allocator, T: type) ![]T { pub fn read(self: @This(), alloc: std.mem.Allocator, T: type) ![]T {
const out = try alloc.alloc(T, @divExact(self.size, @sizeOf(T))); const out = try alloc.alloc(T, @divExact(self.size, @sizeOf(T)));

32
src/GpuTextureView.zig Normal file
View File

@ -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);
}

View File

@ -7,6 +7,7 @@ const GpuArena = gpu.GpuArena;
const GpuBuffer = gpu.GpuBuffer; const GpuBuffer = gpu.GpuBuffer;
const GpuRender = gpu.GpuRender; const GpuRender = gpu.GpuRender;
const GpuTexture = gpu.GpuTexture; const GpuTexture = gpu.GpuTexture;
const GpuTextureView = gpu.GpuTextureView;
pub fn main(init: std.process.Init) !void { pub fn main(init: std.process.Init) !void {
const allocator = init.gpa; const allocator = init.gpa;
@ -42,8 +43,8 @@ pub fn main(init: std.process.Init) !void {
}); });
defer texture.deinit(); defer texture.deinit();
const target_view = c.wgpuTextureCreateView(texture.raw, null) orelse return error.View; const view = try GpuTextureView.init(gloc, texture, .{});
defer c.wgpuTextureViewRelease(target_view); defer view.deinit();
// 4. Create a staging buffer to pull pixels from VRAM to CPU // 4. Create a staging buffer to pull pixels from VRAM to CPU
// 4 bytes per pixel (RGBA8) // 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 })); const cpu_staging_buf = try GpuBuffer.init(gloc, buffer_bytes, .initMany(&.{ .CopyDst, .CopySrc }));
// 5. Draw the Circle Frame into the texture view! // 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 // 6. Copy the texture data into our CPU staging buffer
const enc = c.wgpuDeviceCreateCommandEncoder(device.device, null) orelse return error.Encoder; const enc = c.wgpuDeviceCreateCommandEncoder(device.device, null) orelse return error.Encoder;

View File

@ -5,6 +5,7 @@ pub const GpuDevice = @import("GpuDevice.zig");
pub const GpuCompute = @import("GpuCompute.zig"); pub const GpuCompute = @import("GpuCompute.zig");
pub const GpuRender = @import("GpuRender.zig"); pub const GpuRender = @import("GpuRender.zig");
pub const GpuTexture = @import("GpuTexture.zig"); pub const GpuTexture = @import("GpuTexture.zig");
pub const GpuTextureView = @import("GpuTextureView.zig");
pub const GpuTextureFormat = enum(c_uint) { pub const GpuTextureFormat = enum(c_uint) {
Undefined = 0, 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,
};