Mesh pool storage + mesh asset loader interfaces

This commit is contained in:
Peter Li 2025-04-18 10:49:49 -07:00
parent 67d07367da
commit b9f3f75972
20 changed files with 521 additions and 78 deletions

View File

@ -1,4 +1,3 @@
//!
// MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 -- use this if you're getting that odd crash on mac
b: *std.Build,
nw_builder: *std.Build,

View File

@ -87,7 +87,7 @@ pub const AssetLoaderInterface = struct {
assetType: []const u8,
loadAsset: *const fn (*anyopaque, AssetRef, ?AssetPropertiesBag) AssetLoaderError!void,
destroy: *const fn (*anyopaque, std.mem.Allocator) void,
// destroy: *const fn (*anyopaque, std.mem.Allocator) void,
discardAll: *const fn (*anyopaque) void,
pub fn from(comptime assetType: []const u8, comptime TargetType: type) @This() {
@ -131,7 +131,6 @@ pub const AssetLoaderInterface = struct {
.typeAlign = @alignOf(TargetType),
.loadAsset = wrappedFuncs.loadAsset,
.discardAll = wrappedFuncs.discardAll,
.destroy = wrappedFuncs.destroy,
.assetType = assetType,
};
@ -151,10 +150,6 @@ pub const AssetLoaderRef = struct {
pub fn discardAll(self: @This()) void {
self.vtable.discardAll(self.target);
}
pub fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
self.vtable.destroy(self.target, allocator);
}
};
pub const AssetReferenceSys = struct {
@ -178,6 +173,7 @@ pub const AssetReferenceSys = struct {
.target = loader,
.size = @sizeOf(@TypeOf(loader)),
});
core.engine_log("asset loader for asset type ({s}) registered", .{assetType.utf8()});
}
pub fn loadRef(self: *@This(), asset: AssetRef, propertiesBag: ?AssetPropertiesBag) !void {
@ -201,10 +197,13 @@ pub const AssetReferenceSys = struct {
}
pub fn deinit(self: *@This()) void {
var iter = self.loaders.valueIterator();
while (iter.next()) |i| {
i.destroy(self.allocator);
}
// the loader system shall no longer
// control lifetimes for assset loaders
// each asset loader will be responsible for its own lifetime
// var iter = self.loaders.valueIterator();
// while (iter.next()) |i| {
// i.destroy(self.allocator);
// }
self.loaders.deinit(self.allocator);
}
};

View File

@ -21,6 +21,7 @@ pub fn start_module(comptime programSpec: anytype, args: anytype, allocator: std
}
gSoundEngine = core.gEngine.createObject(NeonSoundEngine, .{ .can_tick = true }) catch unreachable;
gSoundLoader = allocator.create(soundEngine.SoundLoader) catch unreachable;
gSoundLoader.* = soundEngine.SoundLoader.init(gSoundEngine);

View File

@ -37,10 +37,16 @@ var shutdownModuleNames: std.ArrayListUnmanaged([]const u8) = .{};
pub fn start_modules(comptime programSpec: anytype, maybeArgs: ?NwArgs, allocator: std.mem.Allocator) !void {
const Backlog = @This();
var z = core.tracy.ZoneN(@src(), "Starting all Modules");
defer z.End();
inline for (modulelist) |feature| {
if (@hasDecl(Backlog, feature)) {
const Struct = @field(Backlog, feature);
if (comptime core.isModuleEnabled(Struct.Module, programSpec)) {
var z1 = core.tracy.ZoneN(@src(), @ptrCast("Initializing Module"));
defer z1.End();
core.tracy.Message(Struct.Module.name);
if (maybeArgs) |args| {
try Struct.start_module(programSpec, args, allocator);
} else {
@ -84,13 +90,7 @@ pub fn run_everything(comptime GameContext: type) !void {
canTick = true;
}
var gameContext = try core.createObject(GameContext, .{ .can_tick = canTick });
if (@hasDecl(GameContext, "prepare_game")) {
gameContext.prepare_game() catch @panic("Unable to run base level prepare script");
} else if (@hasDecl(GameContext, "prepare")) {
gameContext.prepare() catch @panic("Unable to run base level prepare script");
}
_ = try core.createObject(GameContext, .{ .can_tick = canTick });
try core.gEngine.run();

View File

@ -55,6 +55,8 @@ pub const Engine = struct {
destroyListSimple: ArrayListUnmanaged(EngineObjectRef) = .{},
destroyListCore: ArrayListUnmanaged(EngineObjectRef) = .{},
prepares: ArrayListUnmanaged(EngineObjectRef) = .{},
lastEngineTime: f64,
deltaTime: f64, // delta time for this frame from the previous frame
frameNumber: u64,
@ -127,6 +129,7 @@ pub const Engine = struct {
self.eventors.deinit(self.allocator);
core.engine_logs("destroying tickables");
self.prepares.deinit(self.allocator);
self.tickables.deinit(self.allocator);
self.preTickables.deinit(self.allocator);
self.nfdRuntime.destroy();
@ -169,6 +172,10 @@ pub const Engine = struct {
try self.renderers.append(self.allocator, newObjectRef);
}
if (@hasDecl(T, "prepare")) {
try self.prepares.append(self.allocator, newObjectRef);
}
if (@hasDecl(T, "preTick")) {
try self.preTickables.append(self.allocator, newObjectRef);
}
@ -308,8 +315,15 @@ pub const Engine = struct {
setupFunc(self.platformCtx) catch unreachable;
}
if (self.rendererSetupFunc) |setupFunc| {
setupFunc(self.rendererCtx) catch unreachable;
{
var z = core.tracy.Zone(@src());
for (self.prepares.items) |prep| {
var z1 = core.tracy.ZoneN(@src(), "Preparing game");
core.tracy.Message(prep.vtable.typeName);
prep.vtable.prepare_func.?(prep.ptr) catch unreachable;
z1.End();
}
z.End();
}
while (true) {

View File

@ -51,23 +51,59 @@ pub const EngineObjectVTable = struct {
exitSignal_func: ?*const fn (*anyopaque) EngineDataEventError!void = null,
readyToExit_func: ?*const fn (*anyopaque) bool = null,
prepare_func: ?*const fn (*anyopaque) EngineDataEventError!void = null,
pub fn from(comptime TargetType: type) EngineObjectVTable {
const wrappedInit = struct {
const funcFind: @TypeOf(@field(TargetType, "init")) = @field(TargetType, "init");
pub fn func(allocator: std.mem.Allocator) EngineDataEventError!*anyopaque {
const newObject = funcFind(allocator) catch return error.BadInit;
return @as(*anyopaque, @ptrCast(newObject));
}
};
var self = EngineObjectVTable{
.typeName = @typeName(TargetType),
.typeSize = @sizeOf(TargetType),
.typeAlign = @alignOf(TargetType),
.init_func = wrappedInit.func,
.init_func = undefined,
};
if (@hasDecl(TargetType, "init")) {
const wrappedInit = struct {
const funcFind: @TypeOf(@field(TargetType, "init")) = @field(TargetType, "init");
pub fn func(allocator: std.mem.Allocator) EngineDataEventError!*anyopaque {
const newObject = funcFind(allocator) catch return error.BadInit;
return @as(*anyopaque, @ptrCast(newObject));
}
};
self.init_func = wrappedInit.func;
}
if (@hasDecl(TargetType, "create")) {
const wrappedInit = struct {
const funcFind: @TypeOf(@field(TargetType, "create")) = @field(TargetType, "create");
pub fn func(allocator: std.mem.Allocator) EngineDataEventError!*anyopaque {
const newObject = funcFind(allocator) catch return error.BadInit;
return @as(*anyopaque, @ptrCast(newObject));
}
};
self.init_func = wrappedInit.func;
}
// function used for objects created before engine loop starts;
// this is a defferred startup
//
// will run normally during create if in systems thread
//
// or else will run
if (@hasDecl(TargetType, "prepare")) {
const wrap = struct {
pub fn func(pointer: *anyopaque) EngineDataEventError!void {
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
ptr.prepare() catch return error.BadInit;
}
};
self.prepare_func = wrap.func;
}
if (@hasDecl(TargetType, "postInit")) {
const wrappedPostInit = struct {
pub fn func(pointer: *anyopaque) EngineDataEventError!void {
@ -155,6 +191,17 @@ pub const EngineObjectVTable = struct {
self.deinit_func = wrappedDeinit.func;
}
if (@hasDecl(TargetType, "destroy")) {
const wrappedDeinit = struct {
pub fn func(pointer: *anyopaque) void {
var ptr = @as(*TargetType, @ptrCast(@alignCast(pointer)));
ptr.destroy();
}
};
self.deinit_func = wrappedDeinit.func;
}
return self;
}
};

View File

@ -216,7 +216,9 @@ pub const LoggerSys = struct {
if (builtin.is_test) {
std.debug.print("{s}", .{self.flushBuffer.items});
} else {
try self.consoleFile.writer().writeAll(self.flushBuffer.items);
const writer = self.consoleFile.writer();
try writer.writeAll(self.flushBuffer.items);
try self.consoleFile.writeAll("");
}
self.flushBuffer.clearRetainingCapacity();
self.lock.unlock();

View File

@ -3,6 +3,7 @@ const sdl3 = @import("sdl3");
const dependencyList = [_][]const u8{
"core",
"assets",
"sdl3",
"platform",
"shaderTypes",

View File

@ -0,0 +1,33 @@
mesh: ?meshes.IndexedMesh = null,
textureId: ?u32 = null,
visibility: bool = true,
textureName: core.Name = core.NameInvalid,
meshName: core.Name = core.NameInvalid,
pub var BaseContainer: *MeshSet = undefined;
pub const ComponentName = "Mesh";
pub const ScriptExports: []const []const u8 = &.{
"setMesh",
"setTextureByName",
};
pub fn setMeshByName(self: *@This(), meshName: core.Name) void {
self.meshName = meshName;
}
// script function
pub fn setMesh(self: *@This(), meshName: []const u8) void {
const name = core.MakeName(meshName);
self.mesh = rend.getMeshByName(core.MakeName(meshName));
self.meshName = name;
}
// animator: ?*Animator = null, todo.. implement animator
pub const MeshSet = core.SparseSet(@This());
const meshes = @import("meshes.zig");
const core = @import("core");
const std = @import("std");
const rend = @import("../rend.zig");

View File

@ -42,7 +42,6 @@ pub const JointNameEntry = struct {
index: u32 = 0,
pub fn deinit(self: @This(), allocator: std.mem.Allocator) void {
// const allocator = graphics.getContext().allocator;
allocator.free(self.name);
}
};
@ -54,6 +53,45 @@ pub const IndexedMesh = struct {
jointRemap: ?[]u8, // this is NOT a string, they're joint indices.. which happen to be u8s
};
pub const MeshSourceType = enum { obj, gltf };
pub const LoadMeshSettings = struct {
path: []const u8,
sourceType: ?MeshSourceType = null,
skeletonName: ?core.Name,
};
pub fn loadIndexedMeshForPooling(meshName: core.Name, opt: LoadMeshSettings) !void {
var sourceType = MeshSourceType.gltf;
if (opt.sourceType) |st| {
sourceType = st;
}
// todo.. check if we have a cooked version of that file, if so just load that instead.
// otherwise, load the file
var update: MeshUpdate = undefined;
switch (sourceType) {
.obj => {
update = try loadIndexedMeshForPoolingObj(rend.getAllocator(), meshName, opt.path);
},
.gltf => {
update = try loadIndexedMeshForPoolingGltf(rend.getAllocator(), meshName, opt.skeletonName, opt.path);
},
}
try renderer.pushMeshUpdate(update);
}
const loadIndexedMeshForPoolingGltf = gltfLoader.loadIndexedMeshForPoolingGltf;
const loadIndexedMeshForPoolingObj = objLoader.loadIndexedMeshForPoolingObj;
const gltfLoader = @import("gltfLoader.zig");
const objLoader = @import("objLoader.zig");
const core = @import("core");
const rend = @import("../rend.zig");
const renderer = rend.renderer;
const std = @import("std");
const zgltf = @import("zgltf");

View File

@ -0,0 +1,97 @@
const MeshVertexTransmute = extern struct { data: [@sizeOf(MeshVertex)]u8 };
pub fn loadIndexedMeshForPoolingObj(allocator: std.mem.Allocator, meshName: core.Name, path: []const u8) !MeshUpdate {
const file = try core.fs().loadFile(path);
defer core.fs().unmap(file);
var Objs = try objLoader.loadObjBytes(file.bytes, allocator);
defer Objs.deinit();
var vertexMap = std.AutoHashMap(MeshVertexTransmute, u32).init(allocator);
defer vertexMap.deinit();
var vertexList = std.ArrayList(MeshVertex).init(allocator);
var indexList = std.ArrayList(u32).init(allocator);
const m: *objLoader.ObjMesh = &Objs.meshes.items[0];
// only thing i care about right now is normal and position
for (m.v_faces.items) |f| {
const face: objLoader.ObjFace = f;
if (face.count == 3) {
for (0..face.count) |i| {
const p = m.v_positions.items[face.vertex[i] - 1];
const n = m.v_normals.items[face.normal[i] - 1];
const u = m.v_uvs.items[face.texture[i] - 1];
const meshVertex: MeshVertex = .{
.position = .{ .x = p.x, .y = p.y, .z = p.z },
.normal = .{ .x = n.x, .y = n.y, .z = n.z },
.color = .{ .r = n.x, .g = n.y, .b = n.z, .a = 1.0 },
.uv = .{ .x = u.x, .y = 1 - u.y },
};
var index: u32 = @intCast(vertexList.items.len);
const transmute: MeshVertexTransmute = @bitCast(meshVertex);
if (vertexMap.get(transmute)) |cachedIndex| {
index = cachedIndex;
} else {
try vertexMap.put(transmute, index);
try vertexList.append(meshVertex);
}
try indexList.append(index);
}
}
if (face.count == 4) {
const il: []const usize = &.{ 0, 1, 2, 2, 3, 0 };
for (il) |i| {
const p = m.v_positions.items[face.vertex[i] - 1];
const n = m.v_normals.items[face.normal[i] - 1];
const u = m.v_uvs.items[face.texture[i] - 1];
const meshVertex: MeshVertex = .{
.position = .{ .x = p.x, .y = p.y, .z = p.z },
.normal = .{ .x = n.x, .y = n.y, .z = n.z },
.color = .{ .r = n.x, .g = n.y, .b = n.z, .a = 1.0 },
.uv = .{ .x = u.x, .y = 1 - u.y },
};
var index: u32 = @intCast(vertexList.items.len);
const transmute: MeshVertexTransmute = @bitCast(meshVertex);
if (vertexMap.get(transmute)) |cachedIndex| {
index = cachedIndex;
} else {
try vertexMap.put(transmute, index);
try vertexList.append(meshVertex);
}
try indexList.append(index);
}
}
}
var jointNames = std.ArrayList(JointNameEntry).init(allocator);
const rv: MeshUpdate = .{
.new = .{
.vertices = try vertexList.toOwnedSlice(),
.indices = try indexList.toOwnedSlice(),
.jointNames = try jointNames.toOwnedSlice(),
.skeletonName = null,
.name = meshName,
},
};
core.graphics_log("[{s}] vertex count vertices={d} indices={d}", .{ path, rv.new.vertices.len, rv.new.indices.len });
return rv;
}
const meshes = @import("meshes.zig");
const MeshUpdate = meshes.MeshUpdate;
const JointNameEntry = meshes.JointNameEntry;
const MeshVertex = meshes.MeshVertex;
const core = @import("core");
const std = @import("std");
const objLoader = @import("objLoader");

View File

@ -17,6 +17,14 @@ pub const context = renderer.context; // context getter func
pub const MeshPool = renderer.MeshPool;
pub const getMesh = renderer.getMesh;
pub const getMeshByName = renderer.getMeshByName;
var rendAllocator: std.mem.Allocator = undefined;
pub fn getAllocator() std.mem.Allocator {
return rendAllocator;
}
// controls glfw and general windowing
// graphics depends on this one
pub const Module: core.ModuleDescription = .{
@ -27,7 +35,8 @@ pub const Module: core.ModuleDescription = .{
pub fn start_module(comptime programSpec: anytype, args: anytype, allocator: std.mem.Allocator) !void {
_ = args;
_ = programSpec;
_ = allocator;
core.engine_log("starting up renderer interface", .{});
rendAllocator = allocator;
try renderer.start();
}

View File

@ -0,0 +1,75 @@
// @peterino2
allocator: std.mem.Allocator,
pub var LoaderInterfaceVTable = assets.AssetLoaderInterface.from("Mesh", @This());
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
};
return self;
}
pub fn discardAll(self: *@This()) void {
_ = self;
}
pub fn destroy(self: *@This()) void {
self.allocator.destroy(self);
}
// unfortunately this one is blocking
pub fn loadAsset(self: *@This(), assetRef: assets.AssetRef, propertiesBag: ?assets.AssetPropertiesBag) assets.AssetLoaderError!void {
_ = self;
const sourceType = getSourceType(propertiesBag);
core.engine_log("loading mesh asset {s} [{s}]", .{ propertiesBag.?.path, if (sourceType) |s| @tagName(s) else "default" });
meshes.loadIndexedMeshForPooling(assetRef.name, .{
.path = propertiesBag.?.path,
.sourceType = getSourceType(propertiesBag),
.skeletonName = if (propertiesBag.?.skeletonName) |skName| core.MakeName(skName) else null,
}) catch return error.UnableToLoad;
}
fn getSourceType(propertiesBag: ?assets.AssetPropertiesBag) ?meshes.MeshSourceType {
if (propertiesBag) |bag| {
if (bag.meshType) |meshType| {
if (std.mem.eql(u8, meshType, "obj")) {
return meshes.MeshSourceType.obj;
}
if (std.mem.eql(u8, meshType, "gltf")) {
return meshes.MeshSourceType.gltf;
}
}
// try to deduce it by file name, if nothing is set.
const ext = core.getFileExtension(bag.path);
if (std.mem.eql(u8, ext, ".obj")) {
return meshes.MeshSourceType.obj;
}
if (std.mem.eql(u8, ext, ".gltf")) {
return meshes.MeshSourceType.gltf;
}
if (std.mem.eql(u8, ext, ".glb")) {
return meshes.MeshSourceType.gltf;
}
}
return null;
}
const assets = @import("assets");
const core = @import("core");
const std = @import("std");
const rend = @import("../rend.zig");
const renderer = rend.renderer;
const meshes = rend.meshes;

View File

@ -12,14 +12,21 @@ pub const MeshPool = struct {
device: *gpu.GPUDevice,
destroyList: std.ArrayList(*gpu.GPUTransferBuffer),
installedMeshes: std.AutoHashMapUnmanaged(u32, meshes.IndexedMesh) = .{},
pub fn create(device: *gpu.GPUDevice, allocator: std.mem.Allocator, settings: MeshPoolCreationSettings) !*@This() {
const self = try allocator.create(@This());
gMeshPool = self;
self.* = .{
.allocator = allocator,
.indexSpans = try core.MergedSpans.init(allocator, settings.indexCount),
.vertexSpans = try core.MergedSpans.init(allocator, settings.vertexCount),
.meshUpdates = try core.RingQueue(rend.MeshUpdate).init(allocator, 128),
.destroyList = std.ArrayList(*gpu.GPUTransferBuffer).init(allocator),
.device = device,
};
@ -35,24 +42,106 @@ pub const MeshPool = struct {
.props = 0,
});
core.graphics_log("mesh pool created {d}k vertices, {d}k indices", .{ settings.vertexCount / 1000, settings.indexCount / 1000 });
try assets.gAssetSys.registerLoader(try core.createObject(MeshAssetLoader, .{}));
return self;
}
pub fn onUploadCleanup(p: *anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(p));
for (self.destroyList.items) |transferBuffer| {
self.device.releaseGPUTransferBuffer(transferBuffer);
}
self.destroyList.clearRetainingCapacity();
}
pub fn onUploadInner(self: *@This(), copyPass: *gpu.GPUCopyPass) !void {
self.meshUpdates.lock();
defer self.meshUpdates.unlock();
self.destroyList = std.ArrayList(*gpu.GPUTransferBuffer).init(self.allocator);
while (self.meshUpdates.popFromUnlocked()) |u| {
switch (u) {
.new => |new| {
const indexSpan = try self.addTransfer(copyPass, self.indexBuffer, u32, &self.indexSpans, new.indices);
const vertexSpan = try self.addTransfer(copyPass, self.vertexBuffer, meshes.MeshVertex, &self.vertexSpans, new.vertices);
var name = new.name;
try self.installedMeshes.put(self.allocator, name.handle(), .{
.vertex = vertexSpan,
.index = indexSpan,
.name = new.name,
.jointRemap = null, // joint remaps... parse the installed skeletal meshes to generate this remap
});
},
.free => |free| {
var name = free.name;
if (!self.installedMeshes.remove(name.handle())) {
core.engine_log("unable to find handle from mesh pool.. {s}", .{name.utf8()});
}
self.vertexSpans.removeSpan(free.vertices);
self.indexSpans.removeSpan(free.indices);
},
}
u.deinit(self.allocator);
}
}
pub fn onUpload(p: *anyopaque, copyPass: *gpu.GPUCopyPass) void {
const self: *@This() = @ptrCast(@alignCast(p));
if (self.meshUpdates.count() <= 0)
return;
_ = copyPass;
self.onUploadInner(copyPass) catch unreachable;
}
pub fn addTransfer(
self: *@This(),
copyPass: *gpu.GPUCopyPass,
destBuffer: *gpu.GPUBuffer,
comptime T: type,
mergedSpans: *core.MergedSpans,
uploadSlice: []const T,
) !core.Span {
const newSpan = try mergedSpans.allocate(@intCast(uploadSlice.len));
const upload = self.device.createGPUTransferBuffer(&.{ .usage = .transferbufferusageUpload, .size = @sizeOf(T) * newSpan.size, .props = 0 });
try self.destroyList.append(upload);
var mappedSlice: []T = undefined;
mappedSlice.ptr = @ptrCast(@alignCast(self.device.mapGPUTransferBuffer(upload, false)));
defer self.device.unmapGPUTransferBuffer(upload);
mappedSlice.len = uploadSlice.len;
std.mem.copyForwards(T, mappedSlice, uploadSlice);
//copyPass.uploadToGPUBuffer(source: [*c]const GPUTransferBufferLocation, destination: [*c]const GPUBufferRegion, cycle: bool)
copyPass.uploadToGPUBuffer(
&.{ .transfer_buffer = upload, .offset = 0 },
&.{ .buffer = destBuffer, .offset = newSpan.start, .size = newSpan.size },
false,
);
return newSpan;
}
// pushes the meshUpdate into the pool, either an insert or a free.
pub fn pushMeshUpdate(self: *@This(), meshUpdate: rend.MeshUpdate) !void {
self.meshUpdates.pushLocked(meshUpdate);
try self.meshUpdates.pushLocked(meshUpdate);
}
pub fn destroy(p: *anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(p));
self.device.releaseGPUBuffer(self.indexBuffer);
self.device.releaseGPUBuffer(self.vertexBuffer);
self.installedMeshes.deinit(self.allocator);
self.destroyList.deinit();
self.indexSpans.deinit();
self.meshUpdates.deinit();
self.vertexSpans.deinit();
@ -65,25 +154,27 @@ pub const MeshPool = struct {
};
};
/// === renderer interface implementation ===
var gMeshPool: *MeshPool = undefined;
pub fn getIndexedMesh(name: []const u8) ?rend.IndexedMesh {
pub fn getMesh(name: []const u8) ?rend.IndexedMesh {
var n = core.MakeName(name);
return getIndexedMeshByName(&n);
return getMeshByName(&n);
}
pub fn getIndexedMeshByName(name: *core.Name) ?rend.IndexedMesh {
// var name = _name;
// gMeshPoolBuffer.vertexMapLock.lock();
// defer gMeshPoolBuffer.vertexMapLock.unlock();
// return gMeshPoolBuffer.vertexMap.get(name.handle());
_ = name;
return null;
pub fn getMeshByName(name: *core.Name) ?rend.IndexedMesh {
return gMeshPool.installedMeshes.get(name.handle());
}
pub fn pushMeshUpdate(meshUpdate: rend.MeshUpdate) !void {
try gMeshPool.pushMeshUpdate(meshUpdate);
}
const MeshAssetLoader = @import("MeshAssetLoader.zig");
const sdl3 = @import("sdl3");
const gpu = sdl3.gpu;
const rend = @import("../rend.zig");
const std = @import("std");
const core = @import("core");
const assets = @import("assets");
const meshes = rend.meshes;

View File

@ -24,6 +24,7 @@ pub const Renderer = struct {
meshPool: *MeshPool = undefined,
uploads: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCopyPass) void }) = .{},
uploadCleanup: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque) void }) = .{},
destroys: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque) void }) = .{},
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This());
@ -35,18 +36,19 @@ pub const Renderer = struct {
.allocator = allocator,
};
core.registerRendererSetup(self, engineSetup);
return self;
}
pub fn engineSetup(p: *anyopaque) core.EngineDataEventError!void {
@as(*@This(), @ptrCast(@alignCast(p))).startRenderer() catch return error.BadInit;
pub fn prepare(self: *@This()) core.EngineDataEventError!void {
core.engine_log("starting up renderer interface", .{});
self.startRenderer() catch return error.BadInit;
}
pub fn registerRendererObject(self: *@This(), T: type) !void {
const object = try T.create(self.device, self.allocator, .{});
try self.uploads.append(self.allocator, .{ .ptr = object, .func = T.onUpload });
try self.uploadCleanup.append(self.allocator, .{ .ptr = object, .func = T.onUploadCleanup });
try self.destroys.append(self.allocator, .{ .ptr = object, .func = T.destroy });
}
@ -165,7 +167,8 @@ pub const Renderer = struct {
return rv;
}
pub fn frameUploads(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
pub fn frameUploads(self: *@This()) void {
const cmd = self.device.acquireGPUCommandBuffer();
const buffer = self.device.mapGPUTransferBuffer(self.colorBufferTransfer, true);
var b: [*][4]f32 = @ptrCast(@alignCast(buffer));
@ -187,18 +190,25 @@ pub const Renderer = struct {
for (self.uploads.items) |upload| {
upload.func(upload.ptr, copyPass);
}
if (!cmd.submitGPUCommandBuffer()) {
core.graphics_log("submit gpu command buffer SDL ERROR: {s}", .{sdl3.getError()});
}
for (self.uploadCleanup.items) |uploadCleanup| {
uploadCleanup.func(uploadCleanup.ptr);
}
}
pub fn tick(self: *@This(), dt: f64) void {
self.totalTime += dt;
const cmd = self.device.acquireGPUCommandBuffer();
self.frameUploads(cmd);
self.frameUploads();
var swapchain_texture: *gpu.GPUTexture = undefined;
if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, &swapchain_texture, null, null)) {
var swapchainTexture: *gpu.GPUTexture = undefined;
if (cmd.waitAndAcquireGPUSwapchainTexture(self.window, &swapchainTexture, null, null)) {
var targetInfo: gpu.GPUColorTargetInfo = std.mem.zeroes(gpu.GPUColorTargetInfo);
targetInfo.texture = swapchain_texture;
targetInfo.texture = swapchainTexture;
targetInfo.clear_color = .{ .r = 0.1, .g = 0.1, .b = 0.1, .a = 1.0 };
targetInfo.load_op = .loadopClear;
targetInfo.store_op = .storeopStore;
@ -218,6 +228,7 @@ pub const Renderer = struct {
for (self.destroys.items) |d| {
d.func(d.ptr);
}
self.uploadCleanup.deinit(self.allocator);
self.uploads.deinit(self.allocator);
self.destroys.deinit(self.allocator);
self.allocator.destroy(self);
@ -227,6 +238,22 @@ pub const Renderer = struct {
pub var gRenderer: *Renderer = undefined;
pub var gAllocator: std.mem.Allocator = undefined;
const rend = @import("../rend.zig");
const std = @import("std");
const core = @import("core");
const platform = @import("platform");
const sdl3 = @import("sdl3");
const gpu = sdl3.gpu;
const SSBO_Scene = @import("sample.vert").Scene;
const meshes_vert = @import("meshes.vert");
const MeshVertices = meshes_vert.Scene;
const MeshUniforms = meshes_vert.Uniforms;
pub const mesh_pool = @import("mesh-pool.zig");
pub const MeshPool = mesh_pool.MeshPool;
// ====== renderer API =======
pub fn start() !void {
gRenderer = try core.createObject(Renderer, .{ .can_tick = true, .isCore = true });
gAllocator = gRenderer.allocator;
@ -237,15 +264,7 @@ pub fn shutdown() void {}
pub fn context() *Renderer {
return gRenderer;
}
pub const getMesh = mesh_pool.getMesh;
pub const getMeshByName = mesh_pool.getMeshByName;
const rend = @import("../rend.zig");
pub const mesh_pool = @import("mesh-pool.zig");
pub const MeshPool = mesh_pool.MeshPool;
const std = @import("std");
const core = @import("core");
const platform = @import("platform");
const sdl3 = @import("sdl3");
const gpu = sdl3.gpu;
const SSBO_Scene = @import("sample.vert").Scene;
pub const pushMeshUpdate = mesh_pool.pushMeshUpdate;

View File

@ -189,7 +189,7 @@ pub fn toksIntoFace(toks: anytype) !ObjFace {
while (toks.next()) |tok| {
if (tok.len == 0)
continue;
var face_desc = std.mem.tokenize(u8, tok, "/");
var face_desc = std.mem.tokenizeAny(u8, tok, "/");
var ic: u32 = 0; // ic= inner_count
if (count >= 4) {
continue;
@ -232,7 +232,7 @@ fn parse_line(lineIn: []const u8, allocator: std.mem.Allocator) !LineParseResult
line = line[1..line.len];
}
var tokens = std.mem.tokenize(u8, line, " ");
var tokens = std.mem.tokenizeAny(u8, line, " ");
const first = tokens.next().?;
if (line[0] == '#') {
@ -354,16 +354,16 @@ pub fn fileIntoLines(file_contents: []const u8) std.mem.SplitIterator(u8, .seque
if (file_contents[index] == '\n') {
if (index > 0) {
if (file_contents[index - 1] == '\r') {
return std.mem.split(u8, file_contents, "\r\n");
return std.mem.splitSequence(u8, file_contents, "\r\n");
} else {
return std.mem.split(u8, file_contents, "\n");
return std.mem.splitSequence(u8, file_contents, "\n");
}
} else {
return std.mem.split(u8, file_contents, "\n");
return std.mem.splitSequence(u8, file_contents, "\n");
}
}
}
return std.mem.split(u8, file_contents, "\n");
return std.mem.splitSequence(u8, file_contents, "\n");
}
const ObjContents = struct {

View File

@ -44,5 +44,9 @@ pub fn pollEvent() ?*Event {
return null;
}
pub fn getError() [*c]const u8 {
return c.SDL_GetError();
}
pub const gpu = @import("gpu.zig");
pub const Scancode = @import("scancode.zig").Scancode;

2
lib/tracy/build.zig vendored
View File

@ -9,7 +9,7 @@ pub fn build(b: *std.Build) void {
//const tracy_enabled = b.option(bool, "tracy", "Enables tracy integration") orelse false;
const tracy_enabled: bool = false; //if (b.graph.env_map.hash_map.get("WITH_TRACY") != null) true else false;
const tracy_enabled: bool = true; //if (b.graph.env_map.hash_map.get("WITH_TRACY") != null) true else false;
// if (b.graph.env_map.hash_map.get("WITH_TRACY")) |with_tracy| {
// tracy_enabled = with_tracy;
// }

View File

@ -10,21 +10,34 @@ pub fn init(allocator: std.mem.Allocator) !*@This() {
return self;
}
pub fn prepare_game(self: *@This()) !void {
const assetReferences = [_]assets.AssetImportReference{
assets.MakeImportRefOptions(
"Mesh",
"m_empire",
.{ .path = "meshes/lost_empire.obj" },
),
assets.MakeImportRefOptions(
"Mesh",
"m_fox",
.{ .path = "gltf-samples/Fox/glTF/Fox.gltf" },
),
};
pub fn prepare(self: *@This()) !void {
_ = self;
core.engine_log(">>>>>>> game prepare", .{});
var z = core.tracy.ZoneN(@src(), "PREPARING GAME");
defer z.End();
try core.fs().addContentPath("sampleGame");
try script.loadTypes("scripts");
try script.runScriptFile("scripts/prepare.lua");
try assets.loadList(assetReferences);
const exitInput = try core.ActionBinding.create(core.MakeName("exit"));
exitInput.addKey(.escape, .keyDown);
_ = exitInput.data.addListener(null, onExit);
exitInput.activate();
const results = try rend.gltfLoader.loadIndexedMeshForPoolingGltf(self.allocator, core.MakeName("test"), null, "gltf-samples/Fox/glTF/Fox.gltf");
defer results.deinit(self.allocator);
core.engine_log("loaded fox, vertices: {d}", .{results.new.vertices.len});
core.engine_log("loaded fox, indices: {d}", .{results.new.indices.len});
}
pub fn onExit(ctx: ?*anyopaque, action: core.ActionEvent) void {
@ -54,6 +67,7 @@ pub fn main() anyerror!void {
const std = @import("std");
const backlog = @import("Backlog");
const assets = backlog.assets;
const core = backlog.core;
const rend = backlog.rend;
const script = core.script;

View File

@ -1 +1 @@
print("hello world!")