Consider the following scenario: Loading textures from files.
Here is my take:
const current_exe_dir: []u8 = try std.fs.selfExeDirPathAlloc(allocator);
defer allocator.free(current_exe_dir);
const texture_path = try std.fs.path.joinZ(allocator, &.{current_exe_dir, "textures/container.jpg"});
defer allocator.free(texture_path);
const img = try util.loadStbImgFromFile(texture_path, null);
current_exe_dir
gets reused, but texture_path
can be discarded as soon as I get the texture data. I don’t mind the allocations, but I don’t like the way my code becomes crowded. The only way out I see is creating wrappers like loadStbImgFromFileRelativeToExecPath
, to hide allocs deallocs away.
Any input is appreciated.
If the function is long enough, which I assume it is, you can also use a temporary ArenaAllocator for all local allocations:
const arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
const arenaAllocator = arena.allocator();
const current_exe_dir: []u8 = try std.fs.selfExeDirPathAlloc(arenaAllocator);
// No defer required
const texture_path = try std.fs.path.joinZ(arenaAllocator, &.{current_exe_dir, "textures/container.jpg"});
// No defer required
...
1 Like
So I’d go with both, arena and GPA/C allocators in the same program?
I guess I’ll have to be careful not to pass the arena somewhere I expect to call allocator.free.
Yeah, using multiple different allocators is common practice in Zig.
And you also don’t really need to be careful. If you use the wrong allocator, then the GPA will produce a runtime error in debug mode.
1 Like
I think you should reuse all these allocations (the paths and the image), by using an ArrayList
. It’s way more efficient and you only have one deallocation, which is inside ArrayList.deinit
.
An Arena for storing textures sounds very inefficient.
Also, in a lot of cases, it’s better to just bake the texture into the executable with @embed
.