added split between transparent and opaque renderpasses

This commit is contained in:
peterino2 2025-09-24 01:06:42 -07:00
parent 7664349913
commit c008e5232d
24 changed files with 494 additions and 125 deletions

View File

@ -325,7 +325,7 @@ pub fn defineComponent(comptime Component: type, allocator: std.mem.Allocator) !
const ContainerType = @TypeOf(Component.BaseContainer.*);
Component.BaseContainer = try ContainerType.create(allocator);
core.engine_log("Component container created " ++ @typeName(Component) ++ " @{x}", .{@intFromPtr(Component.BaseContainer)});
core.engine_log("Component container created " ++ @typeName(Component) ++ " @{x} component size = {d} containerType = {s}", .{ @intFromPtr(Component.BaseContainer), @sizeOf(Component), @TypeOf(Component.BaseContainer.*).EcsContainerInterfaceVTable.containerTypeName });
const container = makeEcsContainerRef(Component.BaseContainer);
try registerEcsContainer(container, core.MakeName(Component.ComponentName));

View File

@ -406,7 +406,7 @@ pub const Engine = struct {
pub const NeonObjectParams = struct {
can_tick: ?bool = null,
responds_to_events: bool = false,
isCore: bool = false,
isCore: bool = false, // if set true, object destruction order matters.
};
const Src = std.builtin.SourceLocation;

View File

@ -35,5 +35,6 @@ pub const PrimitiveText = struct {
fontHandle: u32, // this is just a core.Name handle
flags: packed struct {
setSourceGeometry: bool,
debugText: bool = false,
},
};

View File

@ -1251,7 +1251,7 @@ pub const Context = struct {
}
try drawList.append(.{
.node = .{},
.node = .{ .index = @intCast(i) },
.primitive = .{
.Text = .{
.text = LocText.fromUtf8Z(textData),
@ -1263,6 +1263,7 @@ pub const Context = struct {
.fontHandle = fontHandle,
.flags = .{
.setSourceGeometry = false,
.debugText = true,
},
},
},

View File

@ -6,6 +6,7 @@ const dependencyList = [_][]const u8{
"assets",
"sdl3",
"platform",
"sys",
"shaderTypes",
"objLoader",
"zgltf",

View File

@ -5,6 +5,7 @@
.core = .{ .path = "../core" },
.assets = .{ .path = "../assets" },
.platform = .{ .path = "../platform" },
.sys = .{.path = "../sys"},
.sdl3 = .{ .path = "../../lib/sdl3" },
.ozz = .{ .path = "../../lib/ozz" },

View File

@ -187,6 +187,7 @@ PixelOutput main(
float4 s;
int texMode = scene[Instance].textureMode;
int flags = scene[Instance].flags;
if ((texMode & 0xf0) == 0x10) // YUV
{
float3 offset = float3(-0.0625, -0.5, -0.5);
@ -247,10 +248,11 @@ PixelOutput main(
PixelOutput o;
o.Position = float4(WorldPos, 1.0);
o.Normal = float4(ScreenNormal, 1.0);
o.Color = float4(c2, alpha);
o.Emissive = float4(0.0, 0.0, 0.0, 1.0);
o.Emissive = float4(0.0, 0.0, 0.0, 0.0);
if(c2.x > 1.0) o.Emissive.x = c2.x;
if(c2.y > 1.0) o.Emissive.y = c2.y;
if(c2.z > 1.0) o.Emissive.z = c2.z;

View File

@ -133,7 +133,8 @@ float4 main(float2 UV : TEXCOORD0) : SV_Target0
#if 0
float4 ssao = SsaoTexture.Sample(Sampler2, uv);
c = ssao.xyz;
//c.x += (1.0 - ssao.x) * 0.7;
c = ssao;
#else
float3 ssao = blurSSAO(uv);
@ -143,8 +144,6 @@ float4 main(float2 UV : TEXCOORD0) : SV_Target0
//float4 x = SsaoTexture.Sample(Sampler2, uv);
//c = x;
// c = float3(ssao, ssao, ssao);
c.x *= 1.0 + 0.000001 * EmissiveTexture.Sample(Sampler1, uv).x;
c.x *= 1.0 + 0.000001 * ColorTexture.Sample(Sampler0, uv).x;
c.x *= 1.0 + 0.000001 * SsaoTexture.Sample(Sampler2, uv).x;

View File

@ -5,7 +5,7 @@ struct Scene
uint textureMode; // 0 = regular triple,
// 0x10 = video yuv,
// bit 0 == srgb correction
// bit 1 == fullbright
// bit 1 == fullbright // 0x2
// bit 2 = reserved
// bit 3 = reserved
// bit 4 - 7 = color space:

View File

@ -98,7 +98,12 @@ float4 main(float2 UV: TEXCOORD0) : SV_Target0
// float x = length(c);
// c = float4(x, x, x, 1.0);
#if 1
c = occlusion;
#else
c = mul(View, PositionTexture.Sample(Sampler0, uv)).z / 100; // texture(gPosition, offset.xy).z; // get depth value of kernel sample
#endif
//float j = x.z;
//c = float4(x, 1.0);

View File

@ -433,7 +433,6 @@ pub const AnimationSystem = struct {
Animator.allocator = alloc;
core.engine_logs("Animation System initialized");
try core.defineComponent(Animator, alloc);
return self;
}
@ -464,7 +463,6 @@ pub const AnimationSystem = struct {
}
self.slots.deinit();
core.undefineComponent(Animator);
self.arena.deinit();
self.skeletons.deinit(self.backingAllocator);
self.animTracks.deinit(self.backingAllocator);

View File

@ -101,6 +101,52 @@ pub const RenderInfo = struct {
}
};
pub const ParticleTimelineBurst = struct {
count: u32,
};
pub const ParticleTimeline = struct { func: ParticleTimelineFunc = .{} };
pub const ParticleTimelineFunc = union(enum(u8)) { burst: ParticleTimelineBurst };
pub const VfxEmitter = struct {
emitter: ParticleEmitter,
timeline: ParticleTimeline,
parent: u32,
transform: core.Mat,
};
pub const VfxComponent = struct {
// aims to replace the ParticleEmitter
// instead of having just one particle emitter, the VfxComponent manages scenes for multiple particle emitters
pub var BaseContainer: *core.SparseSet(VfxComponent) = undefined;
pub const ComponentName = "rend.VfxComponent";
pub const ScriptExports: []const []const u8 = &.{};
entity: core.Entity = undefined,
emitterList: std.MultiArrayList(VfxEmitter) = .{},
pub fn initECS(self: *@This(), handle: core.SetHandle) void {
self.entity = core.Entity{ .handle = handle };
}
pub fn addEmitter(self: *@This()) *ParticleEmitter {
_ = self;
}
pub fn update(self: *@This(), dt: f64) void {
_ = dt;
_ = self;
}
pub fn deinitECS(self: *@This(), handle: core.SetHandle) void {
for (self.emitters.items) |emitter| {
emitter.deinitECS(handle);
}
}
};
pub const ParticleEmitter = struct {
pub var BaseContainer: *core.SparseSet(ParticleEmitter) = undefined;
pub const ComponentName = "ParticleEmitter";
@ -324,7 +370,7 @@ pub const ParticleEmitter = struct {
var t_whiteName: core.Name = core.DefineName("t_white");
var m_quad: core.Name = core.DefineName("m_screenPlane");
pub fn initECS(self: *@This(), handle: core.SetHandle) void {
pub fn initECS(self: *@This(), handle: core.ObjectHandle) void {
// get the mesh component
self.entity = core.Entity{ .handle = handle };
@ -338,15 +384,21 @@ pub const ParticleEmitter = struct {
}
}
pub fn deinitECS(self: *@This(), handle: core.ObjectHandle) void {
_ = handle;
self.deinit();
}
pub fn deinit(self: *@This()) void {
_ = self;
self.life.deinit(gParticleAllocator);
self.particlesPVA.deinit(gParticleAllocator);
self.finals.deinit(gParticleAllocator);
self.size.deinit(gParticleAllocator);
}
};
var gParticleAllocator: std.mem.Allocator = undefined;
pub const Effect = struct {};
pub const ParticleSystem = struct {
particleArena: std.heap.ArenaAllocator,
allocator: std.mem.Allocator,
@ -366,8 +418,6 @@ pub const ParticleSystem = struct {
ParticleRandRangef.randomEngine = std.Random.DefaultPrng.init(0x1234);
ParticleRandRangef.randomFunc = ParticleRandRangef.randomEngine.random();
try core.defineComponent(ParticleEmitter, allocator);
return self;
}
@ -383,8 +433,6 @@ pub const ParticleSystem = struct {
}
pub fn destroy(self: *@This()) void {
core.undefineComponent(ParticleEmitter);
self.particleArena.deinit();
self.allocator.destroy(self);
}

View File

@ -80,6 +80,8 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me
pub const ComponentList = struct {
pub const _MeshComponent = MeshComponent;
pub const _CameraComponent = CameraComponent;
pub const _Animator = Animator;
pub const _ParticleEmitter = ParticleEmitter;
};
pub fn setupFromModule() void {

View File

@ -15,7 +15,8 @@ pub const Renderer = struct {
shaderformat: gpu.GPUShaderFormat = undefined,
entrypoint: []const u8 = "main",
testPipeline: *gpu.GPUGraphicsPipeline = undefined,
meshPipe: *gpu.GPUGraphicsPipeline = undefined,
opaquePipe: *gpu.GPUGraphicsPipeline = undefined,
transparentPipe: *gpu.GPUGraphicsPipeline = undefined,
scissor: gpu.Rect = undefined, // .{ .x = 0, .y = 0, .w = 1600, .h = 900 },
colorBuffer: *gpu.GPUBuffer = undefined,
@ -40,6 +41,7 @@ pub const Renderer = struct {
preDraws: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque, *gpu.GPUCommandBuffer) void }) = .{},
shaderReloads: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque) void }) = .{},
shaderReloadFinished: std.ArrayListUnmanaged(struct { ptr: *anyopaque, func: *const fn (*anyopaque) void }) = .{},
depthTexture: *gpu.GPUTexture = undefined,
depthFormat: gpu.GPUTextureFormat = .textureformatD32Float,
@ -122,6 +124,12 @@ pub const Renderer = struct {
}
}
pub fn reloadPluginShadersFinished(self: *@This()) void {
for (self.shaderReloadFinished.items) |interface| {
interface.func(interface.ptr);
}
}
pub fn createRenderTargets(self: *@This()) !void {
var gci = std.mem.zeroes(gpu.GPUTextureCreateInfo);
@ -145,6 +153,9 @@ pub const Renderer = struct {
gci.format = self.normalTargetFormat;
self.state.normalTarget = self.device.createGPUTexture(&gci);
self.swapchainTargetFormat = self.device.getGPUSwapchainTextureFormat(self.window);
core.graphics_log("swapchain format: {any}", .{self.swapchainTargetFormat});
}
pub fn startRenderer(self: *@This()) !void {
@ -165,7 +176,8 @@ pub const Renderer = struct {
core.engine_log("sgpu device created, using shader format: {s}", .{self.shaderSuffix});
core.engine_log("using renderer... scientist", .{});
try self.createMeshPipeline();
try self.createOpaquePipeline();
try self.createTransparentsPipeline();
try self.createBuffers();
try self.createDepthTexture();
try self.createRenderTargets();
@ -350,6 +362,7 @@ pub const Renderer = struct {
if (@hasDecl(T, "onShaderReload")) {
try self.shaderReloads.append(self.allocator, .{ .ptr = object, .func = T.onShaderReload });
try self.shaderReloadFinished.append(self.allocator, .{ .ptr = object, .func = T.onShaderReloadFinished });
}
}
@ -480,69 +493,120 @@ pub const Renderer = struct {
self.shadowCastingPipeline = self.device.createGPUGraphicsPipeline(&pci);
}
pub fn createMeshPipeline(self: *@This()) !void {
const vertex = try self.loadShader("meshes.vert", meshes_vert.LoadArgs);
const fragment = try self.loadShader("lit_mesh.frag", lit_mesh_frag.LoadArgs);
const CreatePipelineInfo = struct {
attributes: ?std.ArrayList(gpu.GPUVertexAttribute) = null,
pci: gpu.GPUGraphicsPipelineCreateInfo = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo),
vertexBuffers: std.ArrayListUnmanaged(gpu.GPUVertexBufferDescription) = .{},
colorTargets: std.ArrayListUnmanaged(gpu.GPUColorTargetDescription) = .{},
var pci = std.mem.zeroes(gpu.GPUGraphicsPipelineCreateInfo);
pci.vertex_shader = vertex;
pci.fragment_shader = fragment;
pub fn init() @This() {
return .{};
}
//var attributes = try self.generateVertexAttributeList();
var attributes = try self.addVertexAttributes(&pci);
defer attributes.deinit();
pub fn addMeshPci(self: *@This(), vertexShader: []const u8, fragmentShader: []const u8) !void {
const ctx = context();
const pci = &self.pci;
pci.vertex_input_state = .{
.num_vertex_buffers = 1,
.vertex_buffer_descriptions = &[_]gpu.GPUVertexBufferDescription{
.{ .slot = 0, .pitch = @sizeOf(rend.MeshVertex), .input_rate = .vertexinputrateVertex, .instance_step_rate = 0 },
},
.num_vertex_attributes = @intCast(attributes.items.len),
.vertex_attributes = @ptrCast(attributes.items.ptr),
};
const vertex = try ctx.loadShader(vertexShader, meshes_vert.LoadArgs);
const fragment = try ctx.loadShader(fragmentShader, lit_mesh_frag.LoadArgs);
pci.vertex_shader = vertex;
pci.fragment_shader = fragment;
pci.depth_stencil_state.compare_op = .compareopLess;
pci.depth_stencil_state.enable_depth_test = true;
pci.depth_stencil_state.enable_depth_write = true;
pci.depth_stencil_state.write_mask = 0xff;
self.attributes = try addVertexAttributesFromStruct(rend.MeshVertex, pci);
self.swapchainTargetFormat = self.device.getGPUSwapchainTextureFormat(self.window);
core.graphics_log("swapchain format: {any}", .{self.swapchainTargetFormat});
pci.target_info.has_depth_stencil_target = true;
pci.target_info.depth_stencil_format = self.depthFormat;
pci.target_info.num_color_targets = 4;
pci.target_info.color_target_descriptions = &[4]gpu.GPUColorTargetDescription{
.{
.format = self.hdrTextureFormat,
//.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
.blend_state = std.mem.zeroInit(gpu.GPUColorTargetBlendState, .{
.alpha_blend_op = .blendopAdd,
.color_blend_op = .blendopAdd,
.src_color_blendfactor = .blendfactorSrcAlpha,
.dst_color_blendfactor = .blendfactorOneMinusSrcAlpha,
.src_alpha_blendfactor = .blendfactorSrcAlpha,
.dst_alpha_blendfactor = .blendfactorOneMinusSrcAlpha,
.enable_blend = true,
}),
},
.{
.format = self.hdrTextureFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
.{
.format = self.positionTargetFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
.{
.format = self.normalTargetFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
};
try self.vertexBuffers.appendSlice(ctx.allocator, &[_]gpu.GPUVertexBufferDescription{
.{
.slot = 0,
.pitch = @sizeOf(rend.MeshVertex),
.input_rate = .vertexinputrateVertex,
.instance_step_rate = 0,
},
});
pci.rasterizer_state.fill_mode = .fillmodeFill;
pci.rasterizer_state.cull_mode = .cullmodeBack;
pci.rasterizer_state.front_face = .frontfaceClockwise;
self.meshPipe = self.device.createGPUGraphicsPipeline(&pci);
pci.vertex_input_state = .{
.num_vertex_buffers = 1,
.vertex_buffer_descriptions = self.vertexBuffers.items.ptr,
.num_vertex_attributes = @intCast(self.attributes.?.items.len),
.vertex_attributes = @ptrCast(self.attributes.?.items.ptr),
};
pci.depth_stencil_state.compare_op = .compareopLess;
pci.depth_stencil_state.enable_depth_test = true;
pci.depth_stencil_state.enable_depth_write = true;
pci.depth_stencil_state.write_mask = 0xff;
pci.target_info.has_depth_stencil_target = true;
pci.target_info.depth_stencil_format = ctx.depthFormat;
pci.target_info.num_color_targets = 4;
try self.colorTargets.appendSlice(ctx.allocator, &[4]gpu.GPUColorTargetDescription{
.{
.format = ctx.hdrTextureFormat,
//.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
.blend_state = std.mem.zeroInit(gpu.GPUColorTargetBlendState, .{
.alpha_blend_op = .blendopAdd,
.color_blend_op = .blendopAdd,
.src_color_blendfactor = .blendfactorSrcAlpha,
.dst_color_blendfactor = .blendfactorOneMinusSrcAlpha,
.src_alpha_blendfactor = .blendfactorSrcAlpha,
.dst_alpha_blendfactor = .blendfactorOneMinusSrcAlpha,
.enable_blend = true,
}),
},
.{
.format = ctx.hdrTextureFormat,
// .blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
.blend_state = std.mem.zeroInit(gpu.GPUColorTargetBlendState, .{
.alpha_blend_op = .blendopAdd,
.color_blend_op = .blendopAdd,
.src_color_blendfactor = .blendfactorSrcAlpha,
.dst_color_blendfactor = .blendfactorOneMinusSrcAlpha,
.src_alpha_blendfactor = .blendfactorSrcAlpha,
.dst_alpha_blendfactor = .blendfactorOneMinusSrcAlpha,
.enable_blend = true,
}),
},
.{
.format = ctx.positionTargetFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
.{
.format = ctx.normalTargetFormat,
.blend_state = std.mem.zeroes(gpu.GPUColorTargetBlendState),
},
});
pci.target_info.color_target_descriptions = self.colorTargets.items.ptr;
pci.rasterizer_state.fill_mode = .fillmodeFill;
pci.rasterizer_state.cull_mode = .cullmodeBack;
pci.rasterizer_state.front_face = .frontfaceClockwise;
}
pub fn deinit(self: *@This()) void {
if (self.attributes) |*a| {
a.deinit();
}
self.vertexBuffers.deinit(context().allocator);
self.colorTargets.deinit(context().allocator);
}
};
pub fn createTransparentsPipeline(self: *@This()) !void {
var createInfo = CreatePipelineInfo.init();
try createInfo.addMeshPci("meshes.vert", "lit_mesh.frag");
defer createInfo.deinit();
createInfo.pci.depth_stencil_state.enable_depth_write = false;
self.transparentPipe = self.device.createGPUGraphicsPipeline(&createInfo.pci);
}
pub fn createOpaquePipeline(self: *@This()) !void {
var createInfo = CreatePipelineInfo.init();
try createInfo.addMeshPci("meshes.vert", "lit_mesh.frag");
defer createInfo.deinit();
self.opaquePipe = self.device.createGPUGraphicsPipeline(&createInfo.pci);
}
pub fn createBuffers(self: *@This()) !void {
@ -864,34 +928,39 @@ pub const Renderer = struct {
renderpass.endGPURenderPass();
}
pub fn drawMeshes(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
// can cache this
const targetInfo: [4]gpu.GPUColorTargetInfo = .{
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.targetTexture,
.load_op = .loadopLoad,
.store_op = .storeopStore,
}),
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.emissiveTarget,
.load_op = .loadopClear,
.store_op = .storeopStore,
}),
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.positionTarget,
.load_op = .loadopClear,
.store_op = .storeopStore,
}),
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.normalTarget,
.load_op = .loadopClear,
.store_op = .storeopStore,
}),
};
var meshRenderPassTargetInfo: ?[4]gpu.GPUColorTargetInfo = null;
const depthTarget = std.mem.zeroInit(gpu.GPUDepthStencilTargetInfo, .{
pub fn beginMeshRenderPass(self: *@This(), cmd: *gpu.GPUCommandBuffer, pipeline: *gpu.GPUGraphicsPipeline, clearDepth: bool) *gpu.GPURenderPass {
if (meshRenderPassTargetInfo == null) {
meshRenderPassTargetInfo = .{
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.targetTexture,
.load_op = .loadopLoad,
.store_op = .storeopStore,
}),
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.emissiveTarget,
.load_op = .loadopClear,
.store_op = .storeopStore,
}),
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.positionTarget,
.load_op = .loadopClear,
.store_op = .storeopStore,
}),
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
.texture = self.state.normalTarget,
.load_op = .loadopClear,
.store_op = .storeopStore,
}),
};
}
const targetInfo = meshRenderPassTargetInfo.?;
var depthTarget = std.mem.zeroInit(gpu.GPUDepthStencilTargetInfo, .{
.texture = self.depthTexture,
.load_op = .loadopClear,
.load_op = .loadopLoad,
.store_op = .storeopDontCare,
.stencil_load_op = .loadopDontCare,
.stencil_store_op = .storeopDontCare,
@ -899,6 +968,10 @@ pub const Renderer = struct {
.clear_stencil = 0,
});
if (clearDepth) {
depthTarget.load_op = .loadopClear;
}
self.state.pass = cmd.beginGPURenderPass(&targetInfo, 4, &depthTarget);
const renderpass = self.state.pass.?;
renderpass.setGPUScissor(&self.scissor);
@ -913,10 +986,14 @@ pub const Renderer = struct {
renderpass.bindGPUVertexBuffers(0, &.{ .buffer = self.meshPool.vertexBuffer, .offset = 0 }, 1);
renderpass.bindGPUIndexBuffer(&.{ .buffer = self.meshPool.indexBuffer, .offset = 0 }, .indexelementsize32bit);
renderpass.bindGPUGraphicsPipeline(self.meshPipe);
renderpass.bindGPUGraphicsPipeline(pipeline);
renderpass.bindGPUFragmentSamplers(self.shadowDepthTextureSlot, &.{ .texture = self.shadowDepthTexture, .sampler = self.blockySampler }, 1);
try self.uploadUniforms(cmd);
return renderpass;
}
pub fn drawOpaques(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
const renderpass = self.beginMeshRenderPass(cmd, self.opaquePipe, true);
//rendering each mesh
{
@ -959,6 +1036,13 @@ pub const Renderer = struct {
}
}
renderpass.endGPURenderPass();
self.state.pass = null;
}
pub fn drawTransparents(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
const renderpass = self.beginMeshRenderPass(cmd, self.transparentPipe, false);
for (self.postMesh.items) |interface| {
interface.func(interface.ptr, cmd, renderpass);
}
@ -967,6 +1051,14 @@ pub const Renderer = struct {
self.state.pass = null;
}
pub fn drawMeshes(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
// can cache this
try self.uploadUniforms(cmd);
self.drawOpaques(cmd);
self.drawTransparents(cmd);
}
pub fn renderTransparents(self: *@This(), cmd: *gpu.GPUCommandBuffer) void {
const targetInfo: [4]gpu.GPUColorTargetInfo = .{
std.mem.zeroInit(gpu.GPUColorTargetInfo, .{
@ -1080,6 +1172,8 @@ pub const Renderer = struct {
self.preDraws.deinit(self.allocator);
self.postRenders.deinit(self.allocator);
self.shaderReloads.deinit(self.allocator);
self.shaderReloadFinished.deinit(self.allocator);
self.uploads.deinit(self.allocator);
self.destroys.deinit(self.allocator);
self.allocator.destroy(self);
@ -1123,16 +1217,37 @@ const SkyboxSystem = @import("SkyboxSystem.zig");
// debug api
pub fn reloadShaders() !void {
_ = core.shell.runCmd(context().allocator, &.{ "python", "../tools/scripts/cookShaders.py" }, ".") catch {
core.graphics_logs("shader cook script failed");
if (core.BuildOption("static_build")) {
core.graphics_logs("skipping shader hotreload, not available in static build");
return;
};
try context().createMeshPipeline();
try context().createPostProcessingPipeline();
try context().ssaoSystem.createPipeline();
}
// _ = core.shell.runCmd(context().allocator, &.{ "python", "../tools/scripts/cookShaders.py" }, ".") catch {
// core.graphics_logs("shader cook script failed");
// return;
// };
try sys.start_CompileShaders(null, reloadShadersFinished);
context().reloadPluginShaders();
}
pub fn reloadShadersFinished(ctx: ?*anyopaque) void {
_ = ctx;
context().createOpaquePipeline() catch {
core.engine_errs("unable to rebuild pipeline");
};
context().createTransparentsPipeline() catch {
core.engine_errs("unable to rebuild pipeline");
};
context().createPostProcessingPipeline() catch {
core.engine_errs("unable to rebuild pipeline");
};
context().ssaoSystem.createPipeline() catch {
core.engine_errs("unable to rebuild pipeline");
};
context().reloadPluginShadersFinished();
}
// ====== renderer API =======
pub fn createInstance() !void {
_ = try core.createObject(Renderer, .{ .can_tick = true, .isCore = true });
@ -1199,6 +1314,7 @@ const tracy = core.tracy;
const animationSystem = rend.animationSystem;
const sys = @import("sys");
const builtin = @import("builtin");
pub const SsaoSystem = @import("ssao.zig");
pub const ParticleRenderer = @import("ParticleRenderer.zig");

View File

@ -1,9 +1,13 @@
const std = @import("std");
pub const core = @import("core");
pub const systemProc = @import("systemProc.zig");
pub const SubprocessTask = @import("systemProc.zig").SubprocessTask;
pub const SubprocessTask = systemProc.SubprocessTask;
pub const SystemRunner = systemProc.SystemRunner;
pub const runCommand = SubprocessTask.runCommand;
pub const start_CompileShaders = systemProc.start_CompileShaders;
// TODO get rid of core.ModuleDescription
// it's really not needed anymore with the
// new backlog API
@ -26,8 +30,12 @@ pub fn start_module(spec: *core.SpecVariantMap, args: anytype, allocator: std.me
_ = args;
_ = spec;
_ = allocator;
_ = try core.createObject(SystemRunner, .{ .can_tick = true });
}
pub fn setupFromModule() void {}
pub fn shutdown_module(allocator: std.mem.Allocator) void {
_ = allocator;
}

View File

@ -39,12 +39,12 @@ pub const SubprocessTask = struct {
self.mutex.lock();
self.stderr = .{};
self.stdout = .{};
debugPrint("running task", .{});
core.engine_log("running task", .{});
for (self.args) |arg| {
debugPrint("{s}", .{arg});
core.engine_log("{s}", .{arg});
}
debugPrint("cwd = {s}", .{self.workingDir.?});
core.engine_log("cwd = {s}", .{self.workingDir.?});
self.child = std.process.Child.init(self.args, self.allocator);
self.child.?.stdout_behavior = .Inherit;
self.child.?.stderr_behavior = .Inherit;
@ -77,7 +77,7 @@ pub const SubprocessTask = struct {
self.mutex.lock();
if (self.child) |*child| {
_ = child;
debugPrint("destroying child process", .{});
core.engine_log("destroying child process", .{});
}
self.mutex.unlock();
self.stdout.deinit(self.allocator);
@ -141,3 +141,162 @@ pub const SubprocessTask = struct {
return task;
}
};
fn findRootDir(allocator: std.mem.Allocator) ![]u8 {
var current_dir = std.fs.cwd();
// Start from current directory and walk up
var path_components = std.ArrayList([]const u8).init(allocator);
defer path_components.deinit();
// Try current directory first
current_dir.access("content.txt", .{}) catch {
// content.txt not found, need to walk up
var temp_dir = current_dir;
while (true) {
// Try to go up one directory
const parent_dir = temp_dir.openDir("..", .{}) catch return error.RootNotFound;
temp_dir = parent_dir;
// Check if content.txt exists in parent directory
temp_dir.access("content.txt", .{}) catch {
continue;
};
// Found content.txt, get the absolute path
return try temp_dir.realpathAlloc(allocator, ".");
}
};
// content.txt found in current directory
return try current_dir.realpathAlloc(allocator, ".");
}
pub fn start_CompileShaders(context: ?*anyopaque, callback: ?*const fn (?*anyopaque) void) !void {
const self = core.EngineObject(SystemRunner).get();
const root_dir = findRootDir(self.allocator) catch {
core.engine_log("Could not find BacklogEngine root directory (content.txt not found)", .{});
return;
};
defer self.allocator.free(root_dir);
core.engine_log("Found root directory: {s}", .{root_dir});
const compile_shaders_dir = try std.fs.path.join(self.allocator, &.{ root_dir, "tools", "compileShaders" });
defer self.allocator.free(compile_shaders_dir);
var compile_shaders_name = core.MakeName(compile_shaders_dir);
core.engine_log("CompileShaders directory: {s}", .{compile_shaders_dir});
const exe_path = try std.fs.path.join(self.allocator, &.{ compile_shaders_dir, "zig-out", "bin", "compileShaders.exe" });
defer self.allocator.free(exe_path);
core.engine_log("Looking for executable at: {s}", .{exe_path});
// Check if compileShaders.exe exists
if (std.fs.cwd().access(exe_path, .{})) |_| {
// File exists, run it directly
core.engine_log("CompileShaders executable found, running directly", .{});
try self.commandQueue.pushLocked(.{ .subprocess = .{
.onComplete = callback,
.context = context,
.task = try SubprocessTask.runCommand(
self.allocator,
&.{"zig-out/bin/compileShaders.exe"},
compile_shaders_name.utf8(),
),
} });
} else |_| {
// File doesn't exist, build it first
core.engine_log("CompileShaders executable not found, building first", .{});
try self.commandQueue.pushLocked(.{
.subprocess = .{ .task = try SubprocessTask.runCommand(
self.allocator,
&.{ "zig", "build", "install" },
compile_shaders_name.utf8(),
) },
});
// Then run it
core.engine_log("Queuing compileShaders execution after build", .{});
try self.commandQueue.pushLocked(.{
.subprocess = .{
.context = context,
.onComplete = callback,
.task = try SubprocessTask.runCommand(
self.allocator,
&.{"zig-out/bin/compileShaders.exe"},
compile_shaders_name.utf8(),
),
},
});
}
}
const Task = union(enum(u8)) {
subprocess: struct {
task: *SubprocessTask,
onComplete: ?*const fn (?*anyopaque) void = null,
context: ?*anyopaque = null,
},
};
pub const SystemRunner = struct {
pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "sys.SystemRunner");
allocator: std.mem.Allocator,
commandQueue: core.RingQueue(Task),
activeCommand: ?Task = null,
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try allocator.create(@This());
self.* = .{
.allocator = allocator,
.commandQueue = try core.RingQueue(Task).init(self.allocator, 256),
};
return self;
}
pub fn tick(self: *@This(), dt: f64) void {
_ = dt;
if (self.activeCommand == null) {
if (self.commandQueue.popFromLocked()) |task| {
switch (task) {
.subprocess => |t| {
self.activeCommand = task;
// core.engine_log("starting task {any}", .{t.args});
t.task.run() catch {
self.activeCommand = null;
return;
};
},
//.func => |f| {
//f(self);
// },
}
}
}
if (self.activeCommand) |active| {
if (active.subprocess.task.checkComplete()) {
if (active.subprocess.onComplete) |onComplete| {
onComplete(active.subprocess.context);
}
active.subprocess.task.destroy();
core.engine_logs("task complete");
self.activeCommand = null;
}
}
}
pub fn destroy(self: *@This()) void {
self.commandQueue.deinit();
self.allocator.destroy(self);
}
};

View File

@ -25,6 +25,8 @@ fontTextures: std.AutoHashMapUnmanaged(u32, *rend.Texture) = .{},
textRenderers: std.AutoHashMapUnmanaged(*papyrus.Context, *TextRenderer) = .{},
reloadingShaders: bool = false,
const DrawCommand = union(enum(u8)) {
rect: struct {
ssboIndex: u32,
@ -126,6 +128,12 @@ pub fn create(allocator: std.mem.Allocator) !*@This() {
return self;
}
pub fn tick(self: *@This(), dt: f64) void {
self.screenContext.tick(dt) catch {
core.engine_errs("unable to tick papyrus");
};
}
pub fn setup(self: *@This()) !void {
core.graphics_log("imgui startup", .{});
@ -269,9 +277,15 @@ pub fn postRender(p: *anyopaque, cmd: *gpu.GPUCommandBuffer) void {
}
pub fn onShaderReload(p: *anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(p));
self.reloadingShaders = true;
}
pub fn onShaderReloadFinished(p: *anyopaque) void {
const self: *@This() = @ptrCast(@alignCast(p));
self.createRectPipeline() catch return; // you think i give a remote fuck about leaks? this is a debug function son.
self.createTextPipeline() catch return;
self.reloadingShaders = false;
}
pub fn getTextRenderer(self: *@This(), ctx: *papyrus.Context) ?*TextRenderer {
@ -294,7 +308,17 @@ pub fn getFontSampler(self: *@This(), fontHandle: u32) ?gpu.GPUTextureSamplerBin
}
pub fn drawToTarget(self: *@This(), cmd: *gpu.GPUCommandBuffer, ctx: *papyrus.Context, targetTexture: *gpu.GPUTexture) void {
const stashedDrawDebug = ctx.drawDebug;
if (self.reloadingShaders) {
ctx.pushDebugText("recompiling shaders...", .{}) catch {};
ctx.pushDebugText("...", .{}) catch {};
}
ctx.drawDebug = stashedDrawDebug or self.reloadingShaders;
ctx.makeDrawList(&self.drawList, &self.stringArena) catch return;
ctx.drawDebug = stashedDrawDebug;
const textRenderer = self.getTextRenderer(ctx);

View File

@ -96,6 +96,7 @@ pub const TextRenderer = struct {
textInstances: std.ArrayListUnmanaged(*TextMeshBuffer) = .{},
deadList: std.ArrayListUnmanaged(*TextMeshBuffer) = .{},
assignedBuffers: std.AutoHashMapUnmanaged(papyrus.NodeHandle, *TextMeshBuffer) = .{},
linearBuffers: std.AutoHashMapUnmanaged(papyrus.NodeHandle, *TextMeshBuffer) = .{}, // set of mesh buffers for linearlly allocated text
allocator: std.mem.Allocator,
lastUpdateTime: f64 = 0,
@ -298,8 +299,10 @@ pub const TextRenderer = struct {
pub fn getOrAllocMeshBuffer(self: *@This(), node: papyrus.NodeHandle, text: papyrus.DrawCommand.PrimitiveText) !*TextMeshBuffer {
const textLen = text.text.getRead().len;
const assignedBuffers: *std.AutoHashMapUnmanaged(papyrus.NodeHandle, *TextMeshBuffer) = if (text.flags.debugText) &self.linearBuffers else &self.assignedBuffers;
// 1. get the assigned buffer;
if (self.assignedBuffers.get(node)) |buffer| {
if (assignedBuffers.get(node)) |buffer| {
if (textLen <= buffer.capacity) {
return buffer;
}
@ -307,7 +310,7 @@ pub const TextRenderer = struct {
core.engine_log("recycling text buffer {any}", .{node});
// if the buffer is too small for the text, dont use that one and add it to the dead list.
try self.deadList.append(self.allocator, buffer);
_ = self.assignedBuffers.remove(node);
_ = assignedBuffers.remove(node);
}
// 2. if no text renderer is available, search through the deadList until you find a TextMeshBuffer
@ -318,7 +321,7 @@ pub const TextRenderer = struct {
for (self.deadList.items, 0..) |buffer, i| {
if (textLen <= buffer.capacity) {
try self.assignedBuffers.put(self.allocator, node, buffer);
try assignedBuffers.put(self.allocator, node, buffer);
foundIndex = i;
foundBuffer = buffer;
}
@ -344,7 +347,7 @@ pub const TextRenderer = struct {
core.engine_log("creating text buffer {any} with length {d} for textLen = {d}", .{ node, newBufferLength, textLen });
const newBuffer = try TextMeshBuffer.create(self.allocator, newBufferLength);
try self.assignedBuffers.put(self.allocator, node, newBuffer);
try assignedBuffers.put(self.allocator, node, newBuffer);
try self.textInstances.append(self.allocator, newBuffer);
return newBuffer;
}
@ -369,6 +372,7 @@ pub const TextRenderer = struct {
}
self.geo.destroy();
self.linearBuffers.deinit(self.allocator);
self.assignedBuffers.deinit(self.allocator);
self.textInstances.deinit(self.allocator);
self.allocator.destroy(self);

View File

@ -139,8 +139,6 @@ pub const TBMap = struct {
}
self.colliderSpecs.deinit(self.allocator);
physics.context().system.update(0.001, .{}) catch {};
self.shapes.deinit(self.allocator);
self.root.destroy();

View File

@ -21,6 +21,7 @@ pub fn build(b: *std.Build) void {
sampleGame.setModuleEnabled("audio", true);
sampleGame.setModuleEnabled("physics", true);
sampleGame.setModuleEnabled("ui", true);
sampleGame.setModuleEnabled("sys", true);
sampleGame.addExtraModule("gameExtras");
sampleGame.addExtraModule("videoplayer");
sampleGame.addExtraModule("doomplayer");

View File

@ -159,13 +159,13 @@ fragment main0_out main0(main0_in in [[stage_in]], constant type_Uniforms& Unifo
float4 _289;
if (_272.x > 1.0)
{
float4 _288 = float4(0.0, 0.0, 0.0, 1.0);
float4 _288 = float4(0.0);
_288.x = _272.x;
_289 = _288;
}
else
{
_289 = float4(0.0, 0.0, 0.0, 1.0);
_289 = float4(0.0);
}
float4 _294;
if (_272.y > 1.0)

View File

@ -3,6 +3,7 @@ pub export fn startup(p_allocator: *anyopaque, p_a: ?*anyopaque) bool {
_ = allocator;
imgui.setupFromModule();
platform.setupFromModule();
sys.setupFromModule();
rend.setupFromModule();
backlog.physics.setupFromModule();
// TODO backlog.setupFromModule
@ -70,7 +71,6 @@ pub const ExternGameObject = struct {
core.engine_log("map loaded", .{});
}
pub fn create(allocator: std.mem.Allocator) !*@This() {
const self = try Slack.create(allocator);
self.* = .{
@ -254,6 +254,7 @@ const imgui = backlog.imgui;
const physics = backlog.physics;
const rend = backlog.rend;
const ig = imgui.api;
const sys = backlog.sys;
const ui = backlog.ui;
const platform = backlog.platform;
const extras = @import("gameExtras");